@abi-software/scaffoldvuer 0.2.2 → 0.2.3-alpha

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 +1076 -717
  10. package/dist/scaffoldvuer.common.js.map +1 -1
  11. package/dist/scaffoldvuer.css +1 -1
  12. package/dist/scaffoldvuer.umd.js +1076 -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 +1997 -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
package/src/App.vue CHANGED
@@ -1,714 +1,669 @@
1
- <template>
2
- <div id="app">
3
- <link
4
- rel="stylesheet"
5
- href="https://fonts.googleapis.com/css?family=Asap:400,400i,500,600,700&display=swap"
6
- >
7
- <drop-zone
8
- ref="dropzone"
9
- @files-drop="onFilesDrop"
10
- >
11
- <ScaffoldVuer
12
- ref="scaffold"
13
- class="vuer"
14
- :display-u-i="displayUI"
15
- :url="url"
16
- :help-mode="helpMode"
17
- :display-latest-changes="true"
18
- :display-minimap="displayMinimap"
19
- :display-markers="displayMarkers"
20
- :enableOpenMapUI="true"
21
- :minimap-settings="minimapSettings"
22
- :show-colour-picker="showColourPicker"
23
- :render="render"
24
- :region="region"
25
- :view-u-r-l="viewURL"
26
- :format="format"
27
- @open-map="openMap"
28
- @on-ready="onReady"
29
- @scaffold-selected="onSelected"
30
- @scaffold-navigated="onNavigated"
31
- @timeChanged="updateCurrentTime"
32
- @zinc-object-added="objectAdded"
33
- />
34
- </drop-zone>
35
-
36
- <el-popover
37
- placement="bottom"
38
- trigger="click"
39
- width="500"
40
- class="popover"
41
- :append-to-body="false"
42
- >
43
- <div class="options-container">
44
- <el-row :gutter="20">
45
- <p>{{ selectedCoordinates }}</p>
46
- </el-row>
47
- <el-row
48
- class="app-row"
49
- :gutter="20"
50
- >
51
- <p v-if="currentTime !== 0">
52
- time emited is: {{ currentTime.toFixed(2) }}
53
- </p>
54
- </el-row>
55
- <el-row :gutter="20">
56
- <el-col
57
- :span="4"
58
- :offset="1"
59
- >
60
- <el-switch
61
- v-model="displayUI"
62
- active-text="UI"
63
- />
64
- </el-col>
65
- <el-col
66
- :span="4"
67
- :offset="1"
68
- >
69
- <el-switch
70
- v-model="displayMarkers"
71
- active-text="Markers"
72
- active-icon-class="el-icon-location"
73
- active-color="#8300bf"
74
- />
75
- </el-col>
76
- <el-col
77
- :span="4"
78
- :offset="1"
79
- >
80
- <el-switch
81
- v-model="displayMinimap"
82
- active-text="Minimap"
83
- active-icon-class="el-icon-discover"
84
- active-color="#8300bf"
85
- />
86
- </el-col>
87
- <el-col
88
- :span="6"
89
- :offset="1"
90
- >
91
- <el-switch
92
- v-model="tumbleOn"
93
- active-text="Tumble"
94
- active-color="#8300bf"
95
- />
96
- </el-col>
97
- </el-row>
98
- <el-row :gutter="20">
99
- <el-button
100
- size="mini"
101
- @click="helpMode = !helpMode"
102
- >
103
- Help Mode
104
- </el-button>
105
- <el-button
106
- size="mini"
107
- @click="screenCapture()"
108
- >
109
- Capture
110
- </el-button>
111
- <el-button
112
- size="mini"
113
- @click="featureTesting()"
114
- >
115
- Test 1
116
- </el-button>
117
- <el-button
118
- size="mini"
119
- @click="featureTesting2()"
120
- >
121
- Test 2
122
- </el-button>
123
- </el-row>
124
- <el-row :gutter="10">
125
- <el-button
126
- size="mini"
127
- @click="saveSettings()"
128
- >
129
- Save Settings
130
- </el-button>
131
- <el-button
132
- size="mini"
133
- @click="restoreSettings()"
134
- >
135
- Restore Settings
136
- </el-button>
137
- <el-button
138
- size="mini"
139
- @click="exportGLB()"
140
- >
141
- Export GLB
142
- </el-button>
143
- <el-button
144
- size="mini"
145
- @click="exportGLTF()"
146
- >
147
- Export GLTF
148
- </el-button>
149
- </el-row>
150
- <el-row :gutter="30">
151
- <el-col
152
- :span="7"
153
- :offset="2"
154
- >
155
- <el-switch
156
- v-model="syncMode"
157
- active-text="Sync Mode"
158
- active-color="#8300bf"
159
- />
160
- <el-row v-if="syncMode">
161
- <el-input-number
162
- v-model="zoom"
163
- :min="1.0"
164
- :controls="false"
165
- placeholder="Please input"
166
- label="zoom"
167
- />
168
- <el-input-number
169
- v-model="pos[0]"
170
- :min="-1.0"
171
- :max="1.0"
172
- :controls="false"
173
- placeholder="Please input"
174
- label="x"
175
- />
176
- <el-input-number
177
- v-model="pos[1]"
178
- :min="-1.0"
179
- :max="1.0"
180
- :controls="false"
181
- label="y"
182
- />
183
- </el-row>
184
- </el-col>
185
- </el-row>
186
- <el-row :gutter="30">
187
- <el-col
188
- :span="7"
189
- :offset="4"
190
- >
191
- <el-switch
192
- v-model="render"
193
- active-text="Rendering"
194
- active-color="#8300bf"
195
- />
196
- </el-col>
197
- <el-col
198
- :span="8"
199
- :offset="1"
200
- >
201
- <el-switch
202
- v-model="renderInfoOn"
203
- active-text="Renderer Info"
204
- active-color="#8300bf"
205
- />
206
- </el-col>
207
- </el-row>
208
- <template v-if="renderInfoOn && rendererInfo">
209
- <el-row>
210
- <el-col
211
- v-for="(value, name) in rendererInfo.memory"
212
- :key="name"
213
- :offset="4"
214
- :span="6"
215
- >
216
- {{ name }} : {{ value }}
217
- </el-col>
218
- </el-row>
219
- <el-row>
220
- <el-col
221
- v-for="(value, name) in rendererInfo.render"
222
- :key="name"
223
- :offset="1"
224
- :span="6"
225
- >
226
- {{ name }} : {{ value }}
227
- </el-col>
228
- </el-row>
229
- </template>
230
- <el-input
231
- v-model="input"
232
- type="textarea"
233
- autosize
234
- placeholder="Please input"
235
- style="padding-left: 5%; width: 90%"
236
- />
237
- </div>
238
- <el-button
239
- slot="reference"
240
- icon="el-icon-setting"
241
- @click="setSceneToWindo"
242
- >
243
- Options
244
- </el-button>
245
- </el-popover>
246
- <el-popover
247
- placement="bottom"
248
- trigger="click"
249
- width="800"
250
- class="models-popover"
251
- popper-class="table-popover"
252
- :append-to-body="false"
253
- >
254
- <ModelsTable @viewModelClicked="viewModelClicked" />
255
- <el-button
256
- slot="reference"
257
- icon="el-icon-folder-opened"
258
- >
259
- Models
260
- </el-button>
261
- <el-autocomplete
262
- slot="reference"
263
- v-model="searchText"
264
- class="search-box"
265
- placeholder="Search"
266
- :fetch-suggestions="fetchSuggestions"
267
- :popper-append-to-body="false"
268
- popper-class="autocomplete-popper"
269
- @keyup.enter.native="search(searchText)"
270
- @select="search(searchText)"
271
- />
272
- </el-popover>
273
- </div>
274
- </template>
275
-
276
- <script>
277
- /* eslint-disable no-alert, no-console */
278
- import { ScaffoldVuer } from "./components/index.js";
279
- import DropZone from "./components/DropZone.vue";
280
- import ModelsTable from "./components/ModelsTable.vue";
281
- import Vue from "vue";
282
- import {
283
- Button,
284
- Col,
285
- Icon,
286
- Input,
287
- InputNumber,
288
- Popover,
289
- Row,
290
- Switch,
291
- Autocomplete,
292
- } from "element-ui";
293
- import lang from "element-ui/lib/locale/lang/en";
294
- import locale from "element-ui/lib/locale";
295
- import taShader from "zincjs/src/shaders/textureArray.js";
296
- import volumeRender from "zincjs/src/shaders/volumeRender.js";
297
- import volumeTexture from "zincjs/src/shaders/volumeTexture.js";
298
-
299
- locale.use(lang);
300
- Vue.use(Button);
301
- Vue.use(Col);
302
- Vue.use(Icon);
303
- Vue.use(Input);
304
- Vue.use(InputNumber);
305
- Vue.use(Popover);
306
- Vue.use(Row);
307
- Vue.use(Switch);
308
- Vue.use(Autocomplete);
309
-
310
- let texture_prefix = undefined;
311
-
312
- const getVolumeRender = (texture) => {
313
- const myUniforms = volumeRender.getUniforms();
314
- console.log(myUniforms);
315
- myUniforms.u_size.value.set(
316
- texture.size.width,
317
- texture.size.height,
318
- texture.size.depth
319
- );
320
- myUniforms.u_data.value = texture.impl;
321
- const options = {
322
- fs: volumeRender.fs,
323
- vs: volumeRender.vs,
324
- uniforms: myUniforms,
325
- glslVersion: volumeRender.glslVersion,
326
- };
327
- return options;
328
- };
329
-
330
- const getVolumeTexture = (texture) => {
331
- const myUniforms = volumeTexture.getUniforms();
332
- console.log(myUniforms);
333
- myUniforms.volume_scale.value.set(
334
- texture.size.width / texture.size.depth,
335
- texture.size.height / texture.size.depth,
336
- 1
337
- );
338
- myUniforms.diffuse.value = texture.impl;
339
- myUniforms.depth.value = texture.size.depth;
340
- myUniforms.volume_dims.value = [200, 200, 200];
341
- const options = {
342
- fs: volumeTexture.fs,
343
- vs: volumeTexture.vs,
344
- uniforms: myUniforms,
345
- glslVersion: volumeTexture.glslVersion,
346
- };
347
- return options;
348
- };
349
-
350
- const getTestShader = (texture) => {
351
- const myUniforms = taShader.getUniforms();
352
- myUniforms.depth.value = texture.size.depth;
353
- myUniforms.diffuse.value = texture.impl;
354
- const options = {
355
- fs: taShader.fs,
356
- vs: taShader.vs,
357
- uniforms: myUniforms,
358
- glslVersion: taShader.glslVersion,
359
- };
360
- console.log(options);
361
- return options;
362
- };
363
-
364
- const getTexture = async (scaffoldModule) => {
365
- const imgArray = [];
366
- const texture = new scaffoldModule.Zinc.TextureArray();
367
- for (let i = 1733; i < 1860; i++) {
368
- imgArray.push(`${texture_prefix}/foot${i}.jpg`);
369
- //imgArray.push(`${process.env.VUE_APP_TEXTURE_FOOT_PREFIX}/foot${i}.jpg`);
370
- }
371
- await texture.loadFromImages(imgArray);
372
- return texture;
373
- };
374
-
375
- const testVolume = async (scaffoldModule, objects) => {
376
- if (objects) {
377
- const texture = await getTexture(scaffoldModule);
378
- //const options = getTestShader(texture);
379
- const options = getVolumeTexture(texture);
380
- const material = texture.getMaterial(options);
381
- console.log(material, texture, objects);
382
- objects[0].morph.material = material;
383
- }
384
- };
385
-
386
- const testSlides = async (scaffoldModule) => {
387
- const texture = await getTexture(scaffoldModule);
388
- const textureSlides = new scaffoldModule.Zinc.TextureSlides(texture);
389
- textureSlides.createSlides([
390
- {
391
- direction: "y",
392
- value: 0.1,
393
- },
394
- {
395
- direction: "y",
396
- value: 0.3,
397
- },
398
- {
399
- direction: "y",
400
- value: 0.5,
401
- },
402
- {
403
- direction: "y",
404
- value: 0.7,
405
- },
406
- {
407
- direction: "y",
408
- value: 0.9,
409
- },
410
- {
411
- direction: "x",
412
- value: 0.5,
413
- },
414
- {
415
- direction: "z",
416
- value: 0.5,
417
- },
418
- ]);
419
- scaffoldModule.scene.addZincObject(textureSlides);
420
- };
421
-
422
- export default {
423
- name: "App",
424
- components: {
425
- DropZone,
426
- ScaffoldVuer,
427
- ModelsTable,
428
- },
429
- data: function () {
430
- return {
431
- url: undefined,
432
- input: undefined,
433
- displayUI: true,
434
- selectedCoordinates: undefined,
435
- helpMode: false,
436
- displayMarkers: false,
437
- syncMode: false,
438
- currentTime: 0,
439
- displayMinimap: false,
440
- tumbleOn: false,
441
- showColourPicker: true,
442
- minimapSettings: {
443
- x_offset: 16,
444
- y_offset: 50,
445
- width: 128,
446
- height: 128,
447
- align: "top-right",
448
- },
449
- render: true,
450
- region: "",
451
- viewURL: "",
452
- renderInfoOn: false,
453
- rendererInfo: undefined,
454
- zoom: 1,
455
- pos: [0, 0],
456
- format: "metadata",
457
- sceneSettings: [],
458
- searchInput: "",
459
- searchText: "",
460
- };
461
- },
462
- watch: {
463
- input: function () {
464
- this.parseInput();
465
- },
466
- tumbleOn: function (val) {
467
- this.autoTumble(val);
468
- },
469
- "$route.query": {
470
- handler: "parseQuery",
471
- deep: true,
472
- immediate: true,
473
- },
474
- syncMode: function (val) {
475
- this.$refs.scaffold.toggleSyncControl(val);
476
- },
477
- },
478
- mounted: function () {
479
- this._objects = [];
480
- this.selectedCoordinates =
481
- this.$refs.scaffold.getDynamicSelectedCoordinates();
482
- this.rendererInfo = this.$refs.scaffold.getRendererInfo();
483
- },
484
- created: function () {
485
- texture_prefix = process.env.VUE_APP_TEXTURE_FOOT_PREFIX;
486
- },
487
- methods: {
488
- exportGLTF: function () {
489
- this.$refs.scaffold.exportGLTF(false).then((data) => {
490
- let dataStr =
491
- "data:text/json;charset=utf-8," +
492
- encodeURIComponent(JSON.stringify(data));
493
- let hrefElement = document.createElement("a");
494
- document.body.append(hrefElement);
495
- hrefElement.download = `export.gltf`;
496
- hrefElement.href = dataStr;
497
- hrefElement.click();
498
- hrefElement.remove();
499
- });
500
- },
501
- exportGLB: function () {
502
- this.$refs.scaffold.exportGLTF(true).then((data) => {
503
- let blob = new Blob([data], { type: "octet/stream" });
504
- let url = window.URL.createObjectURL(blob);
505
- let hrefElement = document.createElement("a");
506
- document.body.append(hrefElement);
507
- hrefElement.download = `export.glb`;
508
- hrefElement.href = url;
509
- hrefElement.click();
510
- hrefElement.remove();
511
- });
512
- },
513
- objectAdded: function (zincObject) {
514
- console.log(zincObject);
515
- this._objects.push(zincObject);
516
- },
517
- openMap: function (map) {
518
-
519
- console.log(map)
520
- },
521
- featureTesting: async function () {
522
- //Test texture
523
- testVolume(this.$refs.scaffold.$module, this._objects);
524
- },
525
- featureTesting2: async function () {
526
- //Test texture
527
- //testVolume(this.$refs.scaffold.$module, this._objects);
528
- testSlides(this.$refs.scaffold.$module);
529
- },
530
- saveSettings: function () {
531
- this.sceneSettings.push(this.$refs.scaffold.getState());
532
- },
533
- restoreSettings: function () {
534
- if (this.sceneSettings.length > 0)
535
- this.$refs.scaffold.setState(this.sceneSettings.pop());
536
- },
537
- viewModelClicked: function (location) {
538
- this.input = location;
539
- },
540
- screenCapture: function () {
541
- this.$refs.scaffold.captureScreenshot("capture.png");
542
- },
543
- setSceneToWindo: function () {
544
- window.scene = this.$refs.scaffold.$module.scene;
545
- },
546
- search: function (term) {
547
- this.$refs.scaffold.search(term, true);
548
- },
549
- fetchSuggestions: function (term, cb) {
550
- if ( term === "" || !this.$refs.scaffold ) {
551
- cb([]);
552
- }
553
- cb(
554
- this.$refs.scaffold.fetchSuggestions(term).map((item) => {
555
- return {
556
- value: item.suggestion,
557
- label: item.suggestion,
558
- };
559
- })
560
- );
561
- console.log(
562
- "found suggestions",
563
- this.$refs.scaffold.fetchSuggestions(term)
564
- );
565
- },
566
- autoTumble: function (flag) {
567
- let cameracontrol =
568
- this.$refs.scaffold.$module.scene.getZincCameraControls();
569
- if (flag) {
570
- this.displayUI = false;
571
- cameracontrol.enableAutoTumble();
572
- cameracontrol.autoTumble([1.0, 0.0], Math.PI / 2, true);
573
- } else {
574
- this.displayUI = true;
575
- cameracontrol.stopAutoTumble();
576
- }
577
- },
578
- onReady: function () {
579
- console.log("ready");
580
- //window.scaffoldvuer = this.$refs.scaffold;
581
- this.$refs.dropzone.revokeURLs();
582
- },
583
- onSelected: function (data) {
584
- console.log(data);
585
- if (data && data.length > 0 && data[0].data.group) {
586
- delete this.$route.query["viewURL"];
587
- this.$refs.scaffold.showRegionTooltip(data[0].data.group, true, true);
588
- //this.$router.replace({
589
- // query: { ...this.$route.query, region: data[0].data.group }
590
- //});
591
- }
592
- },
593
- onNavigated: function (data) {
594
- this.zoom = data.zoom;
595
- this.pos[0] = data.target[0];
596
- this.pos[1] = data.target[1];
597
- },
598
- onFilesDrop: function (payload) {
599
- if (payload.format == "gltf") this.format = "gltf";
600
- else this.format = "metadata";
601
- this.input = payload.url;
602
- },
603
- parseInput: function () {
604
- if (this.$route.query.url !== this.input) {
605
- const queries = { ...this.$route.query };
606
- if (this.input && this.input !== "") queries.url = this.input;
607
- this.$router.replace({
608
- query: { ...this.$route.query, url: this.input },
609
- });
610
- }
611
- },
612
- updateCurrentTime: function (val) {
613
- this.currentTime = val;
614
- },
615
- parseQuery: function (query) {
616
- if (query.url != this.url) {
617
- this._objects = [];
618
- }
619
- if (query.url) {
620
- this.url = query.url;
621
- } else {
622
- this.url =
623
- "https://mapcore-bucket1.s3-us-west-2.amazonaws.com/others/29_Jan_2020/heartICN_metadata.json";
624
- }
625
- if (this.url.includes(".gltf") || this.url.includes(".glb")) {
626
- this.format = "gltf";
627
- } else if (this.url.includes(".json")) {
628
- this.format = "metadata";
629
- }
630
- this.input = this.url;
631
- if (query.region) {
632
- this.region = query.region;
633
- } else {
634
- this.region = "";
635
- }
636
- if (query.viewURL) {
637
- this.viewURL = query.viewURL;
638
- } else {
639
- this.viewURL = "";
640
- }
641
- },
642
- },
643
- };
644
- </script>
645
-
646
- <style lang="scss">
647
- @import "~element-ui/packages/theme-chalk/src/button";
648
- @import "~element-ui/packages/theme-chalk/src/col";
649
- @import "~element-ui/packages/theme-chalk/src/icon";
650
- @import "~element-ui/packages/theme-chalk/src/input";
651
- @import "~element-ui/packages/theme-chalk/src/switch";
652
- @import "~element-ui/packages/theme-chalk/src/popover";
653
- @import "~element-ui/packages/theme-chalk/src/row";
654
-
655
- #app {
656
- font-family: "Asap", sans-serif;
657
- -webkit-font-smoothing: antialiased;
658
- -moz-osx-font-smoothing: grayscale;
659
- text-align: center;
660
- color: #2c3e50;
661
- height: 100%;
662
- width: 100%;
663
- position: absolute;
664
- overflow: hidden;
665
- }
666
-
667
- body {
668
- margin: 0px;
669
- }
670
-
671
- .options-container {
672
- text-align: center;
673
- .el-row {
674
- margin-bottom: 8px;
675
- &:last-child {
676
- margin-bottom: 0;
677
- }
678
- }
679
- }
680
-
681
- .vuer {
682
- position: absolute;
683
- width: 100%;
684
- height: 100%;
685
- }
686
-
687
- .popover {
688
- top: 5px;
689
- right: 10px;
690
- position: absolute;
691
- }
692
-
693
- .app-row {
694
- .el-row {
695
- margin-bottom: 5px;
696
- &:last-child {
697
- margin-bottom: 0;
698
- }
699
- }
700
- }
701
-
702
- .models-popover {
703
- top: 5px;
704
- position: absolute;
705
- }
706
-
707
- .table-popover {
708
- opacity: 0.9;
709
- }
710
-
711
- svg.map-icon {
712
- color: $app-primary-color;
713
- }
714
- </style>
1
+ <template>
2
+ <div id="app">
3
+ <link
4
+ rel="stylesheet"
5
+ href="https://fonts.googleapis.com/css?family=Asap:400,400i,500,600,700&display=swap"
6
+ />
7
+ <drop-zone ref="dropzone" @files-drop="onFilesDrop">
8
+ <ScaffoldVuer
9
+ ref="scaffold"
10
+ class="vuer"
11
+ :display-u-i="displayUI"
12
+ :url="url"
13
+ :help-mode="helpMode"
14
+ :display-latest-changes="true"
15
+ :display-minimap="displayMinimap"
16
+ :display-markers="displayMarkers"
17
+ :enableOpenMapUI="true"
18
+ :minimap-settings="minimapSettings"
19
+ :show-colour-picker="showColourPicker"
20
+ :render="render"
21
+ :region="region"
22
+ :view-u-r-l="viewURL"
23
+ :format="format"
24
+ :marker-labels="markerLabels"
25
+ @open-map="openMap"
26
+ @on-ready="onReady"
27
+ @scaffold-selected="onSelected"
28
+ @scaffold-navigated="onNavigated"
29
+ @timeChanged="updateCurrentTime"
30
+ @zinc-object-added="objectAdded"
31
+ />
32
+ </drop-zone>
33
+
34
+ <el-popover
35
+ placement="bottom"
36
+ trigger="click"
37
+ width="500"
38
+ class="popover"
39
+ :append-to-body="false"
40
+ >
41
+ <div class="options-container">
42
+ <el-row :gutter="20">
43
+ <p>{{ selectedCoordinates }}</p>
44
+ </el-row>
45
+ <el-row :gutter="20">
46
+ <p v-if="currentTime !== 0">
47
+ time emited is: {{ currentTime.toFixed(2) }}
48
+ </p>
49
+ </el-row>
50
+ <el-row :gutter="20">
51
+ <el-col :span="4" :offset="1">
52
+ <el-switch v-model="displayUI" active-text="UI" />
53
+ </el-col>
54
+ <el-col :span="4" :offset="1">
55
+ <el-switch
56
+ v-model="displayMarkers"
57
+ active-text="Markers"
58
+ active-icon-class="el-icon-location"
59
+ active-color="#8300bf"
60
+ />
61
+ </el-col>
62
+ <el-col :span="4" :offset="1">
63
+ <el-switch
64
+ v-model="displayMinimap"
65
+ active-text="Minimap"
66
+ active-icon-class="el-icon-discover"
67
+ active-color="#8300bf"
68
+ />
69
+ </el-col>
70
+ </el-row>
71
+ <el-row :gutter="20">
72
+ <el-col :span="6">
73
+ <el-switch
74
+ v-model="tumbleOn"
75
+ active-text="Tumble"
76
+ active-color="#8300bf"
77
+ />
78
+ </el-col>
79
+ <el-col :span="1"> x: </el-col>
80
+ <el-col :span="3">
81
+ <el-input-number
82
+ class="tumble-direction"
83
+ controls-position="right"
84
+ v-model="tumbleDirection[0]"
85
+ :min="-1.0"
86
+ :max="1.0"
87
+ :controls="false"
88
+ placeholder="Please input"
89
+ label="x"
90
+ @change="autoTumble"
91
+ />
92
+ </el-col>
93
+ <el-col :span="1" :offset="1"> y: </el-col>
94
+ <el-col :span="3">
95
+ <el-input-number
96
+ class="tumble-direction"
97
+ controls-position="right"
98
+ v-model="tumbleDirection[1]"
99
+ :min="-1.0"
100
+ :max="1.0"
101
+ :controls="false"
102
+ placeholder="Please input"
103
+ label="y"
104
+ @change="autoTumble"
105
+ />
106
+ </el-col>
107
+ </el-row>
108
+ <el-row :gutter="20">
109
+ <el-button size="mini" @click="helpMode = !helpMode">
110
+ Help Mode
111
+ </el-button>
112
+ <el-button size="mini" @click="screenCapture()"> Capture </el-button>
113
+ <el-button size="mini" @click="changeMarkers"> Change Markers </el-button>
114
+ </el-row>
115
+ <el-row :gutter="10">
116
+ <el-button size="mini" @click="saveSettings()">
117
+ Save Settings
118
+ </el-button>
119
+ <el-button size="mini" @click="restoreSettings()">
120
+ Restore Settings
121
+ </el-button>
122
+ <el-button size="mini" @click="exportGLB()"> Export GLB </el-button>
123
+ <el-button size="mini" @click="exportGLTF()"> Export GLTF </el-button>
124
+ </el-row>
125
+ <el-row :gutter="30">
126
+ <el-col :span="7" :offset="2">
127
+ <el-switch
128
+ v-model="syncMode"
129
+ active-text="Sync Mode"
130
+ active-color="#8300bf"
131
+ />
132
+ <el-row v-if="syncMode">
133
+ <el-input-number
134
+ v-model="zoom"
135
+ :min="1.0"
136
+ :controls="false"
137
+ placeholder="Please input"
138
+ label="zoom"
139
+ />
140
+ <el-input-number
141
+ v-model="pos[0]"
142
+ :min="-1.0"
143
+ :max="1.0"
144
+ :controls="false"
145
+ placeholder="Please input"
146
+ label="x"
147
+ />
148
+ <el-input-number
149
+ v-model="pos[1]"
150
+ :min="-1.0"
151
+ :max="1.0"
152
+ :controls="false"
153
+ label="y"
154
+ />
155
+ </el-row>
156
+ </el-col>
157
+ </el-row>
158
+ <el-row :gutter="30">
159
+ <el-col :span="7" :offset="4">
160
+ <el-switch
161
+ v-model="render"
162
+ active-text="Rendering"
163
+ active-color="#8300bf"
164
+ />
165
+ </el-col>
166
+ <el-col :span="8" :offset="1">
167
+ <el-switch
168
+ v-model="renderInfoOn"
169
+ active-text="Renderer Info"
170
+ active-color="#8300bf"
171
+ />
172
+ </el-col>
173
+ </el-row>
174
+ <template v-if="renderInfoOn && rendererInfo">
175
+ <el-row>
176
+ <el-col
177
+ v-for="(value, name) in rendererInfo.memory"
178
+ :key="name"
179
+ :offset="4"
180
+ :span="6"
181
+ >
182
+ {{ name }} : {{ value }}
183
+ </el-col>
184
+ </el-row>
185
+ <el-row>
186
+ <el-col
187
+ v-for="(value, name) in rendererInfo.render"
188
+ :key="name"
189
+ :offset="1"
190
+ :span="6"
191
+ >
192
+ {{ name }} : {{ value }}
193
+ </el-col>
194
+ </el-row>
195
+ </template>
196
+ <el-row :gutter="20">
197
+ Feature Demo:
198
+ <el-button size="mini" @click="featureTextureVolume(false)">
199
+ Texture volume
200
+ </el-button>
201
+ <el-button size="mini" @click="featureTextureSlides(false)">
202
+ Texture slides
203
+ </el-button>
204
+ <el-button size="mini" @click="featureTextureVolume(true)">
205
+ Body volume
206
+ </el-button>
207
+ <el-button size="mini" @click="featureTextureSlides(true)">
208
+ Body slides
209
+ </el-button>
210
+ <el-switch
211
+ v-model="onClickMarkers"
212
+ active-text="Markers On Selection"
213
+ active-color="#8300bf"
214
+ />
215
+ </el-row>
216
+ <el-row :gutter="20">
217
+ <el-input
218
+ v-model="input"
219
+ type="textarea"
220
+ autosize
221
+ placeholder="Please input"
222
+ style="padding-left: 5%; width: 90%"
223
+ />
224
+ </el-row>
225
+ </div>
226
+ <el-button
227
+ slot="reference"
228
+ icon="el-icon-setting"
229
+ @click="setSceneToWindo"
230
+ >
231
+ Options
232
+ </el-button>
233
+ </el-popover>
234
+ <el-popover
235
+ placement="bottom"
236
+ trigger="click"
237
+ width="800"
238
+ class="models-popover"
239
+ popper-class="table-popover"
240
+ :append-to-body="false"
241
+ >
242
+ <ModelsTable @viewModelClicked="viewModelClicked" />
243
+ <el-button slot="reference" icon="el-icon-folder-opened">
244
+ Models
245
+ </el-button>
246
+ <el-autocomplete
247
+ slot="reference"
248
+ v-model="searchText"
249
+ class="search-box"
250
+ placeholder="Search"
251
+ :fetch-suggestions="fetchSuggestions"
252
+ :popper-append-to-body="false"
253
+ popper-class="autocomplete-popper"
254
+ @keyup.enter.native="search(searchText)"
255
+ @select="search(searchText)"
256
+ >
257
+ <template slot-scope="{ item }">
258
+ <div class="value">
259
+ {{ item.value }}
260
+ </div>
261
+ </template>
262
+ </el-autocomplete>
263
+ </el-popover>
264
+ </div>
265
+ </template>
266
+
267
+ <script>
268
+ /* eslint-disable no-alert, no-console */
269
+ import { ScaffoldVuer } from "./components/index.js";
270
+ import DropZone from "./app/DropZone.vue";
271
+ import ModelsTable from "./app/ModelsTable.vue";
272
+ import {testSlides, testVolume} from "./app/TextureDemos.js";
273
+ import Vue from "vue";
274
+ import {
275
+ Button,
276
+ Col,
277
+ Icon,
278
+ Input,
279
+ InputNumber,
280
+ Popover,
281
+ Row,
282
+ Switch,
283
+ Autocomplete,
284
+ } from "element-ui";
285
+ import lang from "element-ui/lib/locale/lang/en";
286
+ import locale from "element-ui/lib/locale";
287
+
288
+ locale.use(lang);
289
+ Vue.use(Button);
290
+ Vue.use(Col);
291
+ Vue.use(Icon);
292
+ Vue.use(Input);
293
+ Vue.use(InputNumber);
294
+ Vue.use(Popover);
295
+ Vue.use(Row);
296
+ Vue.use(Switch);
297
+ Vue.use(Autocomplete);
298
+
299
+ let texture_prefix = undefined;
300
+
301
+ export default {
302
+ name: "App",
303
+ components: {
304
+ DropZone,
305
+ ScaffoldVuer,
306
+ ModelsTable,
307
+ },
308
+ data: function () {
309
+ return {
310
+ url: undefined,
311
+ input: undefined,
312
+ displayUI: true,
313
+ selectedCoordinates: undefined,
314
+ helpMode: false,
315
+ displayMarkers: false,
316
+ onClickMarkers: false,
317
+ syncMode: false,
318
+ currentTime: 0,
319
+ displayMinimap: false,
320
+ tumbleOn: false,
321
+ tumbleDirection: [1.0, 0.0],
322
+ showColourPicker: true,
323
+ minimapSettings: {
324
+ x_offset: 16,
325
+ y_offset: 50,
326
+ width: 128,
327
+ height: 128,
328
+ align: "top-right",
329
+ },
330
+ markerLabels: ["left atrium", "epicardium"],
331
+ render: true,
332
+ region: "",
333
+ viewURL: "",
334
+ renderInfoOn: false,
335
+ rendererInfo: undefined,
336
+ zoom: 1,
337
+ pos: [0, 0],
338
+ format: "metadata",
339
+ sceneSettings: [],
340
+ searchInput: "",
341
+ searchText: "",
342
+ loadTextureVolumeOnReady: false,
343
+ readyCallback: undefined,
344
+ };
345
+ },
346
+ watch: {
347
+ input: function () {
348
+ this.parseInput();
349
+ },
350
+ tumbleOn: function () {
351
+ this.autoTumble();
352
+ },
353
+ "$route.query": {
354
+ handler: "parseQuery",
355
+ deep: true,
356
+ immediate: true,
357
+ },
358
+ syncMode: function (val) {
359
+ this.$refs.scaffold.toggleSyncControl(val);
360
+ },
361
+ },
362
+ mounted: function () {
363
+ this._objects = [];
364
+ this.selectedCoordinates =
365
+ this.$refs.scaffold.getDynamicSelectedCoordinates();
366
+ this.rendererInfo = this.$refs.scaffold.getRendererInfo();
367
+ },
368
+ created: function () {
369
+ texture_prefix = process.env.VUE_APP_TEXTURE_FOOT_PREFIX;
370
+ },
371
+ methods: {
372
+ exportGLTF: function () {
373
+ this.$refs.scaffold.exportGLTF(false).then((data) => {
374
+ let dataStr =
375
+ "data:text/json;charset=utf-8," +
376
+ encodeURIComponent(JSON.stringify(data));
377
+ let hrefElement = document.createElement("a");
378
+ document.body.append(hrefElement);
379
+ hrefElement.download = `export.gltf`;
380
+ hrefElement.href = dataStr;
381
+ hrefElement.click();
382
+ hrefElement.remove();
383
+ });
384
+ },
385
+ exportGLB: function () {
386
+ this.$refs.scaffold.exportGLTF(true).then((data) => {
387
+ let blob = new Blob([data], { type: "octet/stream" });
388
+ let url = window.URL.createObjectURL(blob);
389
+ let hrefElement = document.createElement("a");
390
+ document.body.append(hrefElement);
391
+ hrefElement.download = `export.glb`;
392
+ hrefElement.href = url;
393
+ hrefElement.click();
394
+ hrefElement.remove();
395
+ });
396
+ },
397
+ objectAdded: function (zincObject) {
398
+ if (this._objects.length === 0)
399
+ zincObject.setMarkerMode("on");
400
+ console.log(zincObject);
401
+ this._objects.push(zincObject);
402
+ },
403
+ openMap: function (map) {
404
+ console.log(map);
405
+ },
406
+ featureTextureVolume: async function (overlap) {
407
+ //this.$refs.scaffold.clearScene();
408
+ //volume matrix to fit the human body
409
+ //[-100, 0, 0, 0, 0, -100, 0, 0, 0, 0, -100, 0, -60, -100, 30, 1]
410
+ if (overlap) {
411
+ const url =
412
+ "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/WholeBody/6-match-2023/human/nerve_metadata.json";
413
+ if (this.$route.query.url !== encodeURI(url)) {
414
+ this.$router.replace({ query: { url } });
415
+ this.readyCallback = testVolume;
416
+ return;
417
+ } else {
418
+ testVolume(this.$refs.scaffold, texture_prefix);
419
+ return;
420
+ }
421
+ }
422
+ this.$refs.scaffold.clearScene();
423
+ testVolume(this.$refs.scaffold, texture_prefix);
424
+ },
425
+ featureTextureSlides: async function (overlap) {
426
+ //Test texture
427
+ if (overlap) {
428
+ const url =
429
+ "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/WholeBody/6-match-2023/human/nerve_metadata.json";
430
+ if (this.$route.query.url !== encodeURI(url)) {
431
+ this.$router.replace({ query: { url } });
432
+ this.readyCallback = testSlides;
433
+ return;
434
+ } else {
435
+ testSlides(this.$refs.scaffold, texture_prefix);
436
+ return;
437
+ }
438
+ }
439
+ this.$refs.scaffold.clearScene();
440
+ testSlides(this.$refs.scaffold, texture_prefix);
441
+ },
442
+ saveSettings: function () {
443
+ this.sceneSettings.push(this.$refs.scaffold.getState());
444
+ },
445
+ restoreSettings: function () {
446
+ if (this.sceneSettings.length > 0)
447
+ this.$refs.scaffold.setState(this.sceneSettings.pop());
448
+ },
449
+ viewModelClicked: function (location) {
450
+ this.input = location;
451
+ },
452
+ screenCapture: function () {
453
+ this.$refs.scaffold.captureScreenshot("capture.png");
454
+ },
455
+ setSceneToWindo: function () {
456
+ window.scene = this.$refs.scaffold.$module.scene;
457
+ },
458
+ search: function (term) {
459
+ this.$refs.scaffold.search(term, true);
460
+ },
461
+ fetchSuggestions: function (term, cb) {
462
+ if (term === "" || !this.$refs.scaffold) {
463
+ cb([]);
464
+ }
465
+ cb(
466
+ this.$refs.scaffold.fetchSuggestions(term).map((item) => {
467
+ const value = item.terms.length > 1 ? item.terms[1] : item.terms[0];
468
+ return {
469
+ value: value,
470
+ label: value
471
+ };
472
+ })
473
+ );
474
+ console.log(
475
+ "found suggestions",
476
+ this.$refs.scaffold.fetchSuggestions(term)
477
+ );
478
+ },
479
+ autoTumble: function () {
480
+ const flag = this.tumbleOn;
481
+ let cameracontrol =
482
+ this.$refs.scaffold.$module.scene.getZincCameraControls();
483
+ if (flag) {
484
+ this.displayUI = false;
485
+ cameracontrol.enableAutoTumble();
486
+ if (this.tumbleDirection[0] === 0 && this.tumbleDirection[1] === 0) {
487
+ this.tumbleDirection[0] = 1;
488
+ }
489
+ cameracontrol.autoTumble(this.tumbleDirection, Math.PI / 2, true);
490
+ } else {
491
+ this.displayUI = true;
492
+ cameracontrol.stopAutoTumble();
493
+ }
494
+ },
495
+ onReady: function () {
496
+ console.log("ready");
497
+ //window.scaffoldvuer = this.$refs.scaffold;
498
+ this.$refs.dropzone.revokeURLs();
499
+ if (this.readyCallback) {
500
+ this.readyCallback(this.$refs.scaffold, texture_prefix);
501
+ this.readyCallback = undefined;
502
+ }
503
+ },
504
+ onSelected: function (data) {
505
+ if (data && data.length > 0 && data[0].data.group) {
506
+ delete this.$route.query["viewURL"];
507
+ this.$refs.scaffold.showRegionTooltipWithAnnotations(data, true, true);
508
+ if (this.onClickMarkers) this.$refs.scaffold.setMarkerModeForObjectsWithName(data[0].data.group, "on");
509
+ //this.$router.replace({
510
+ // query: { ...this.$route.query, region: data[0].data.group }
511
+ //});
512
+ }
513
+ },
514
+ changeMarkers: function() {
515
+ if (this.markerLabels[0] === "right ventricle"){
516
+ this.markerLabels = ["left atrium", "epicardium", "stomach"]
517
+ } else {
518
+ this.markerLabels = ["right ventricle"]
519
+ }
520
+ },
521
+ onNavigated: function (data) {
522
+ this.zoom = data.zoom;
523
+ this.pos[0] = data.target[0];
524
+ this.pos[1] = data.target[1];
525
+ },
526
+ onFilesDrop: function (payload) {
527
+ if (payload.format == "gltf") this.format = "gltf";
528
+ else this.format = "metadata";
529
+ this.input = payload.url;
530
+ },
531
+ parseInput: function () {
532
+ if (this.$route.query.url !== this.input) {
533
+ const queries = { ...this.$route.query };
534
+ if (this.input && this.input !== "") queries.url = this.input;
535
+ this.$router.replace({
536
+ query: { ...this.$route.query, url: this.input },
537
+ });
538
+ }
539
+ },
540
+ updateCurrentTime: function (val) {
541
+ this.currentTime = val;
542
+ },
543
+ parseQuery: function (query) {
544
+ if (query.url != this.url) {
545
+ this._objects = [];
546
+ }
547
+ if (query.url) {
548
+ this.url = query.url;
549
+ } else {
550
+ this.url =
551
+ "https://mapcore-bucket1.s3-us-west-2.amazonaws.com/others/29_Jan_2020/heartICN_metadata.json";
552
+ }
553
+ if (this.url.includes(".gltf") || this.url.includes(".glb")) {
554
+ this.format = "gltf";
555
+ } else if (this.url.includes(".json")) {
556
+ this.format = "metadata";
557
+ }
558
+ this.input = this.url;
559
+ if (query.region) {
560
+ this.region = query.region;
561
+ } else {
562
+ this.region = "";
563
+ }
564
+ if (query.viewURL) {
565
+ this.viewURL = query.viewURL;
566
+ } else {
567
+ this.viewURL = "";
568
+ }
569
+ },
570
+ },
571
+ };
572
+ </script>
573
+
574
+ <style lang="scss">
575
+ @import "~element-ui/packages/theme-chalk/src/button";
576
+ @import "~element-ui/packages/theme-chalk/src/col";
577
+ @import "~element-ui/packages/theme-chalk/src/icon";
578
+ @import "~element-ui/packages/theme-chalk/src/input";
579
+ @import "~element-ui/packages/theme-chalk/src/input-number";
580
+ @import "~element-ui/packages/theme-chalk/src/switch";
581
+ @import "~element-ui/packages/theme-chalk/src/popover";
582
+ @import "~element-ui/packages/theme-chalk/src/row";
583
+
584
+ #app {
585
+ font-family: "Asap", sans-serif;
586
+ -webkit-font-smoothing: antialiased;
587
+ -moz-osx-font-smoothing: grayscale;
588
+ text-align: center;
589
+ color: #2c3e50;
590
+ height: 100%;
591
+ width: 100%;
592
+ position: absolute;
593
+ overflow: hidden;
594
+ }
595
+
596
+ body {
597
+ margin: 0px;
598
+ }
599
+
600
+ .options-container {
601
+ text-align: center;
602
+ .el-row {
603
+ margin-bottom: 8px;
604
+ &:last-child {
605
+ margin-bottom: 0;
606
+ }
607
+ }
608
+ }
609
+
610
+ .vuer {
611
+ position: absolute;
612
+ width: 100%;
613
+ height: 100%;
614
+ }
615
+
616
+ .popover {
617
+ top: 5px;
618
+ right: 10px;
619
+ position: absolute;
620
+ }
621
+
622
+ .options-container {
623
+ .el-row {
624
+ margin-bottom: 10px;
625
+ &:last-child {
626
+ margin-bottom: 0;
627
+ }
628
+ }
629
+ }
630
+
631
+ .autocomplete-popper {
632
+ li {
633
+ line-height: normal;
634
+ padding: 7px;
635
+
636
+ .value {
637
+ text-align: left;
638
+ white-space: initial;
639
+ }
640
+ }
641
+ }
642
+
643
+ .models-popover {
644
+ top: 5px;
645
+ position: absolute;
646
+ }
647
+
648
+ .tumble-direction {
649
+ height: 20px;
650
+ .el-input {
651
+ width: 80px;
652
+ height: 20px;
653
+ padding: 0;
654
+ input {
655
+ padding: 0px;
656
+ height: 20px;
657
+ vertical-align: top;
658
+ }
659
+ }
660
+ }
661
+
662
+ .table-popover {
663
+ opacity: 0.9;
664
+ }
665
+
666
+ svg.map-icon {
667
+ color: $app-primary-color;
668
+ }
669
+ </style>