@abi-software/scaffoldvuer 1.21.1-beta.2 → 2.0.0

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 (45) hide show
  1. package/README.md +31 -12
  2. package/dist/index.html +9 -6
  3. package/dist/scaffoldvuer.js +14095 -13154
  4. package/dist/scaffoldvuer.umd.cjs +48 -48
  5. package/dist/style.css +1 -1
  6. package/eslint.config.js +37 -9
  7. package/package.json +10 -3
  8. package/public/index.html +9 -6
  9. package/src/App.vue +312 -279
  10. package/src/ScaffoldVuer-wc.js +4 -4
  11. package/src/app/DropZone.vue +11 -27
  12. package/src/app/ModelsInformation.js +22 -19
  13. package/src/app/ModelsTable.vue +42 -65
  14. package/src/app/TextureDemos.js +38 -64
  15. package/src/assets/_variables.scss +12 -10
  16. package/src/components/GlyphsControls.vue +3 -23
  17. package/src/components/LinesControls.vue +27 -66
  18. package/src/components/OpacityControls.vue +22 -26
  19. package/src/components/PointsControls.vue +20 -71
  20. package/src/components/PrimitiveControls.vue +35 -49
  21. package/src/components/ScaffoldOverlay.vue +63 -63
  22. package/src/components/ScaffoldTooltip.vue +36 -49
  23. package/src/components/ScaffoldTreeControls.vue +81 -129
  24. package/src/components/ScaffoldVuer.vue +509 -498
  25. package/src/components/TextureSlidesControls.vue +22 -64
  26. package/src/components/TransformationControls.vue +15 -42
  27. package/src/components/index.js +2 -2
  28. package/src/main.js +16 -18
  29. package/src/scripts/Annotation.js +3 -3
  30. package/src/scripts/BaseModule.js +26 -30
  31. package/src/scripts/EventNotifier.js +30 -36
  32. package/src/scripts/GraphicsHighlight.js +43 -50
  33. package/src/scripts/LocalFilesHelper.js +27 -26
  34. package/src/scripts/MappedNerves.js +2139 -697
  35. package/src/scripts/MappedOrgans.js +24 -25
  36. package/src/scripts/OrgansRenderer.js +445 -425
  37. package/src/scripts/RendererModule.js +133 -122
  38. package/src/scripts/Search.js +135 -151
  39. package/src/scripts/Utilities.js +129 -127
  40. package/src/scripts/WebGL.js +57 -91
  41. package/src/scripts/niftiHelper.js +3 -3
  42. package/src/store/index.js +7 -7
  43. package/vite.config.js +51 -50
  44. package/vite.static-build.js +7 -7
  45. package/vuese-generator.js +48 -56
package/src/App.vue CHANGED
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <div id="app">
3
- <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Asap:400,400i,500,600,700&display=swap" />
3
+ <link
4
+ rel="stylesheet"
5
+ href="https://fonts.googleapis.com/css?family=Asap:400,400i,500,600,700&display=swap"
6
+ />
4
7
  <drop-zone ref="dropzone" @files-drop="onFilesDrop">
5
8
  <ScaffoldVuer
6
9
  v-if="url"
@@ -38,8 +41,7 @@
38
41
  @zinc-object-added="objectAdded"
39
42
  @vue:mounted="viewerMounted"
40
43
  :usageConfig="usageConfig"
41
- >
42
- </ScaffoldVuer>
44
+ ></ScaffoldVuer>
43
45
  </drop-zone>
44
46
 
45
47
  <HelpModeDialog
@@ -50,7 +52,13 @@
50
52
  @show-next="onHelpModeShowNext"
51
53
  @finish-help-mode="onFinishHelpMode"
52
54
  />
53
- <el-popover popper-class="options-container" placement="bottom" trigger="hover" width="500" :teleported="false">
55
+ <el-popover
56
+ popper-class="options-container"
57
+ placement="bottom"
58
+ trigger="hover"
59
+ width="500"
60
+ :teleported="false"
61
+ >
54
62
  <div>
55
63
  <el-row :gutter="20">
56
64
  <el-col>
@@ -60,9 +68,7 @@
60
68
 
61
69
  <el-row :gutter="20" v-if="currentTime !== 0">
62
70
  <el-col>
63
- <p>
64
- time emited is: {{ currentTime.toFixed(2) }}
65
- </p>
71
+ <p>time emited is: {{ currentTime.toFixed(2) }}</p>
66
72
  </el-col>
67
73
  </el-row>
68
74
 
@@ -71,16 +77,28 @@
71
77
  <el-switch v-model="displayUI" active-text="UI" />
72
78
  </el-col>
73
79
  <el-col :span="auto">
74
- <el-switch v-model="displayMarkers" active-text="Markers" active-icon-class="el-icon-location"
75
- active-color="#8300bf" />
80
+ <el-switch
81
+ v-model="displayMarkers"
82
+ active-text="Markers"
83
+ active-icon-class="el-icon-location"
84
+ active-color="#8300bf"
85
+ />
76
86
  </el-col>
77
87
  <el-col :span="auto">
78
- <el-switch v-model="markerCluster" active-text="Marker Cluster" active-icon-class="el-icon-location"
79
- active-color="#8300bf" />
88
+ <el-switch
89
+ v-model="markerCluster"
90
+ active-text="Marker Cluster"
91
+ active-icon-class="el-icon-location"
92
+ active-color="#8300bf"
93
+ />
80
94
  </el-col>
81
95
  <el-col :span="auto">
82
- <el-switch v-model="displayMinimap" active-text="Minimap" active-icon-class="el-icon-discover"
83
- active-color="#8300bf" />
96
+ <el-switch
97
+ v-model="displayMinimap"
98
+ active-text="Minimap"
99
+ active-icon-class="el-icon-discover"
100
+ active-color="#8300bf"
101
+ />
84
102
  </el-col>
85
103
  </el-row>
86
104
 
@@ -90,19 +108,37 @@
90
108
  </el-col>
91
109
  <el-col :span="auto">
92
110
  <el-row>
93
- <el-col :span="8"> x: </el-col>
111
+ <el-col :span="8">x:</el-col>
94
112
  <el-col :span="16">
95
- <el-input-number class="tumble-direction" controls-position="right" v-model="tumbleDirection[0]" :min="-1.0"
96
- :max="1.0" :controls="false" placeholder="Please input" aria-label="x" @change="autoTumble" />
113
+ <el-input-number
114
+ class="tumble-direction"
115
+ controls-position="right"
116
+ v-model="tumbleDirection[0]"
117
+ :min="-1.0"
118
+ :max="1.0"
119
+ :controls="false"
120
+ placeholder="Please input"
121
+ aria-label="x"
122
+ @change="autoTumble"
123
+ />
97
124
  </el-col>
98
125
  </el-row>
99
126
  </el-col>
100
127
  <el-col :span="auto">
101
128
  <el-row>
102
- <el-col :span="8"> y: </el-col>
129
+ <el-col :span="8">y:</el-col>
103
130
  <el-col :span="16">
104
- <el-input-number class="tumble-direction" controls-position="right" v-model="tumbleDirection[1]" :min="-1.0"
105
- :max="1.0" :controls="false" placeholder="Please input" aria-label="y" @change="autoTumble" />
131
+ <el-input-number
132
+ class="tumble-direction"
133
+ controls-position="right"
134
+ v-model="tumbleDirection[1]"
135
+ :min="-1.0"
136
+ :max="1.0"
137
+ :controls="false"
138
+ placeholder="Please input"
139
+ aria-label="y"
140
+ @change="autoTumble"
141
+ />
106
142
  </el-col>
107
143
  </el-row>
108
144
  </el-col>
@@ -110,9 +146,7 @@
110
146
 
111
147
  <el-row :gutter="20" justify="center" align="middle">
112
148
  <el-col :span="auto">
113
- <el-button size="small" @click="helpMode = !helpMode">
114
- Help Mode
115
- </el-button>
149
+ <el-button size="small" @click="helpMode = !helpMode">Help Mode</el-button>
116
150
  </el-col>
117
151
  <el-col :span="auto">
118
152
  <el-button type="primary" size="small" @click="screenshotDialog = true">
@@ -123,20 +157,16 @@
123
157
 
124
158
  <el-row :gutter="20" justify="center" align="middle">
125
159
  <el-col :span="auto">
126
- <el-button size="small" @click="saveSettings()">
127
- Save Settings
128
- </el-button>
160
+ <el-button size="small" @click="saveSettings()">Save Settings</el-button>
129
161
  </el-col>
130
162
  <el-col :span="auto">
131
- <el-button size="small" @click="restoreSettings()">
132
- Restore Settings
133
- </el-button>
163
+ <el-button size="small" @click="restoreSettings()">Restore Settings</el-button>
134
164
  </el-col>
135
165
  <el-col :span="auto">
136
- <el-button size="small" @click="exportGLB()"> Export GLB </el-button>
166
+ <el-button size="small" @click="exportGLB()">Export GLB</el-button>
137
167
  </el-col>
138
168
  <el-col :span="auto">
139
- <el-button size="small" @click="exportGLTF()"> Export GLTF </el-button>
169
+ <el-button size="small" @click="exportGLTF()">Export GLTF</el-button>
140
170
  </el-col>
141
171
  </el-row>
142
172
 
@@ -147,21 +177,21 @@
147
177
  </el-button>
148
178
  </el-col>
149
179
  <el-col :span="auto">
150
- <el-button size="small">
151
- <label for="annotations-upload">Import Annotations</label>
152
- <input
153
- id="annotations-upload"
154
- type="file"
155
- accept="application/json"
156
- @change="importOfflineAnnotations"
157
- />
158
- </el-button>
180
+ <el-button size="small">
181
+ <label for="annotations-upload">Import Annotations</label>
182
+ <input
183
+ id="annotations-upload"
184
+ type="file"
185
+ accept="application/json"
186
+ @change="importOfflineAnnotations"
187
+ />
188
+ </el-button>
159
189
  </el-col>
160
190
  </el-row>
161
191
 
162
192
  <el-row :gutter="20" justify="center" align="middle">
163
193
  <el-col :span="auto">
164
- <el-button size="small" @click="() => fitBoundingBox=!fitBoundingBox">
194
+ <el-button size="small" @click="() => (fitBoundingBox = !fitBoundingBox)">
165
195
  {{ fitBoundingBox ? 'Unfit' : 'Fit' }} BoundingBox
166
196
  </el-button>
167
197
  </el-col>
@@ -177,15 +207,15 @@
177
207
  <el-button size="small" @click="enableAxisDisplay(true, false)">
178
208
  Enable CoordSystem
179
209
  </el-button>
180
- <el-button size="small" @click="enableAxisDisplay(true, true )">
210
+ <el-button size="small" @click="enableAxisDisplay(true, true)">
181
211
  Enable MiniAxes CoordSystem
182
212
  </el-button>
183
213
  </el-button-group>
184
214
  </el-col>
185
215
  <el-col :span="auto">
186
- <el-button size="small" @click="enableAxisDisplay(false, false)">
187
- Disable CoordSystem
188
- </el-button>
216
+ <el-button size="small" @click="enableAxisDisplay(false, false)">
217
+ Disable CoordSystem
218
+ </el-button>
189
219
  </el-col>
190
220
  </el-row>
191
221
 
@@ -193,7 +223,11 @@
193
223
  <el-col>
194
224
  <el-row :gutter="20" justify="center" align="middle">
195
225
  <el-col>
196
- <el-switch v-model="positionalRotation" active-text="Rotation Helper" active-color="#8300bf" />
226
+ <el-switch
227
+ v-model="positionalRotation"
228
+ active-text="Rotation Helper"
229
+ active-color="#8300bf"
230
+ />
197
231
  </el-col>
198
232
  </el-row>
199
233
  </el-col>
@@ -206,9 +240,7 @@
206
240
  <el-col :span="auto">
207
241
  <el-switch v-model="renderInfoOn" active-text="Renderer Info" active-color="#8300bf" />
208
242
  </el-col>
209
- <el-button size="small" @click="PrintViewport()">
210
- Print Viewport
211
- </el-button>
243
+ <el-button size="small" @click="PrintViewport()">Print Viewport</el-button>
212
244
  </el-row>
213
245
 
214
246
  <template v-if="renderInfoOn && rendererInfo">
@@ -243,24 +275,16 @@
243
275
  </el-button>
244
276
  </el-col>
245
277
  <el-col :span="auto">
246
- <el-button size="small" @click="featureTextureVolume(true)">
247
- Body volume
248
- </el-button>
278
+ <el-button size="small" @click="featureTextureVolume(true)">Body volume</el-button>
249
279
  </el-col>
250
280
  <el-col :span="auto">
251
- <el-button size="small" @click="featureTextureSlides(true)">
252
- Body slides
253
- </el-button>
281
+ <el-button size="small" @click="featureTextureSlides(true)">Body slides</el-button>
254
282
  </el-col>
255
283
  <el-col :span="auto">
256
- <el-button size="small" @click="featureArmSlides(true)">
257
- Arm slides
258
- </el-button>
284
+ <el-button size="small" @click="featureArmSlides(true)">Arm slides</el-button>
259
285
  </el-col>
260
286
  <el-col :span="auto">
261
- <el-button size="small" @click="multipleNifti()">
262
- Time x Nifti
263
- </el-button>
287
+ <el-button size="small" @click="multipleNifti()">Time x Nifti</el-button>
264
288
  </el-col>
265
289
  </el-row>
266
290
  <el-row :gutter="20" justify="center" align="middle">
@@ -293,29 +317,37 @@
293
317
  />
294
318
  </el-col>
295
319
  </el-row>
296
-
297
320
  </div>
298
321
  <template #reference>
299
- <el-button class="options-button" :icon="ElIconSetting">
300
- Options
301
- </el-button>
322
+ <el-button class="options-button" :icon="ElIconSetting">Options</el-button>
302
323
  </template>
303
324
  </el-popover>
304
- <el-popover placement="bottom" trigger="hover" width="800" popper-class="table-popover" :teleported="false">
325
+ <el-popover
326
+ placement="bottom"
327
+ trigger="hover"
328
+ width="800"
329
+ popper-class="table-popover"
330
+ :teleported="false"
331
+ >
305
332
  <template #default>
306
333
  <Suspense>
307
334
  <ModelsTable @viewModelClicked="viewModelClicked" />
308
335
  </Suspense>
309
336
  </template>
310
337
  <template #reference>
311
- <el-button class="models-button control-layer" :icon="ElIconFolderOpened">
312
- Models
313
- </el-button>
338
+ <el-button class="models-button control-layer" :icon="ElIconFolderOpened">Models</el-button>
314
339
  </template>
315
340
  </el-popover>
316
- <el-autocomplete v-model="searchText" class="search-box control-layer" placeholder="Search" :fetch-suggestions="fetchSuggestions"
317
- :teleported="false" popper-class="autocomplete-popper" @keyup.enter="search(searchText)"
318
- @select="search(searchText)">
341
+ <el-autocomplete
342
+ v-model="searchText"
343
+ class="search-box control-layer"
344
+ placeholder="Search"
345
+ :fetch-suggestions="fetchSuggestions"
346
+ :teleported="false"
347
+ popper-class="autocomplete-popper"
348
+ @keyup.enter="search(searchText)"
349
+ @select="search(searchText)"
350
+ >
319
351
  <template #default="{ item }">
320
352
  <div class="value">
321
353
  {{ item.value }}
@@ -328,45 +360,43 @@
328
360
  width="450px"
329
361
  destroy-on-close
330
362
  >
331
- <el-form
332
- ref="formRef"-
333
- :model="screenshotData"
334
- :rules="rules"
335
- label-width="100px"
336
- status-icon
337
- >
338
- <!-- Filename Field -->
339
- <el-form-item label="Filename" prop="filename">
340
- <el-input
341
- v-model="screenshotData.filename"
342
- placeholder="scaffold"
343
- >
344
- <template #append>.png</template>
345
- </el-input>
346
- </el-form-item>
363
+ <el-form
364
+ ref="formRef"
365
+ -
366
+ :model="screenshotData"
367
+ :rules="rules"
368
+ label-width="100px"
369
+ status-icon
370
+ >
371
+ <!-- Filename Field -->
372
+ <el-form-item label="Filename" prop="filename">
373
+ <el-input v-model="screenshotData.filename" placeholder="scaffold">
374
+ <template #append>.png</template>
375
+ </el-input>
376
+ </el-form-item>
347
377
 
348
- <!-- Width Field -->
349
- <el-form-item label="Width (px)" prop="width">
350
- <el-input-number
351
- v-model="screenshotData.width"
352
- :min="100"
353
- :max="7680"
354
- :step="100"
355
- controls-position="right"
356
- style="width: 100%"
357
- />
358
- </el-form-item>
378
+ <!-- Width Field -->
379
+ <el-form-item label="Width (px)" prop="width">
380
+ <el-input-number
381
+ v-model="screenshotData.width"
382
+ :min="100"
383
+ :max="7680"
384
+ :step="100"
385
+ controls-position="right"
386
+ style="width: 100%"
387
+ />
388
+ </el-form-item>
359
389
 
360
- <!-- Height Field -->
361
- <el-form-item label="Height (px)" prop="height">
362
- <el-input-number
363
- v-model="screenshotData.height"
364
- :min="100"
365
- :max="4320"
366
- :step="100"
367
- controls-position="right"
368
- style="width: 100%"
369
- />
390
+ <!-- Height Field -->
391
+ <el-form-item label="Height (px)" prop="height">
392
+ <el-input-number
393
+ v-model="screenshotData.height"
394
+ :min="100"
395
+ :max="4320"
396
+ :step="100"
397
+ controls-position="right"
398
+ style="width: 100%"
399
+ />
370
400
  </el-form-item>
371
401
  </el-form>
372
402
 
@@ -374,9 +404,7 @@
374
404
  <template #footer>
375
405
  <span class="dialog-footer">
376
406
  <el-button @click="screenCancel">Cancel</el-button>
377
- <el-button type="primary" :loading="loading" @click="screenCapture">
378
- Capture
379
- </el-button>
407
+ <el-button type="primary" :loading="loading" @click="screenCapture">Capture</el-button>
380
408
  </span>
381
409
  </template>
382
410
  </el-dialog>
@@ -384,63 +412,53 @@
384
412
  </template>
385
413
 
386
414
  <script>
387
- /* eslint-disable no-alert, no-console */
388
- import { AnnotationService } from '@abi-software/sparc-annotation'
389
- import { ElMessage } from 'element-plus';
415
+ import { AnnotationService } from '@abi-software/sparc-annotation';
390
416
  import { markRaw, shallowRef } from 'vue';
391
- import { ScaffoldVuer } from "./components/index.js";
392
- import DropZone from "./app/DropZone.vue";
393
- import ModelsTable from "./app/ModelsTable.vue";
394
- import { testArmSlides, testSlides, testVolume } from "./app/TextureDemos.js";
417
+ import { ScaffoldVuer } from './components/index.js';
418
+ import DropZone from './app/DropZone.vue';
419
+ import ModelsTable from './app/ModelsTable.vue';
420
+ import { testArmSlides, testSlides, testVolume } from './app/TextureDemos.js';
395
421
  import {
396
422
  FolderOpened as ElIconFolderOpened,
397
423
  Setting as ElIconSetting,
398
- } from '@element-plus/icons-vue'
424
+ } from '@element-plus/icons-vue';
399
425
  import {
400
- ElAutocomplete as Autocomplete,
401
- ElButton as Button,
402
- ElCol as Col,
403
- ElIcon as Icon,
404
- ElInput as Input,
405
- ElInputNumber as InputNumber,
406
- ElPopover as Popover,
407
- ElRow as Row,
408
- ElUpload as Upload,
409
- ElSwitch as Switch,
410
- } from "element-plus";
411
- import { useRoute, useRouter } from 'vue-router'
412
- import { HelpModeDialog } from '@abi-software/map-utilities'
413
- import '@abi-software/map-utilities/dist/style.css'
426
+ ElAutocomplete,
427
+ ElButton,
428
+ ElCol,
429
+ ElInput,
430
+ ElInputNumber,
431
+ ElPopover,
432
+ ElRow,
433
+ ElSwitch,
434
+ } from 'element-plus';
435
+ import { useRoute, useRouter } from 'vue-router';
436
+ import { HelpModeDialog } from '@abi-software/map-utilities';
437
+ import '@abi-software/map-utilities/dist/style.css';
414
438
 
415
439
  let texture_prefix = undefined;
416
440
 
417
441
  const writeTextFile = (filename, data) => {
418
- let dataStr =
419
- "data:text/json;charset=utf-8," +
420
- encodeURIComponent(JSON.stringify(data));
421
- let hrefElement = document.createElement("a");
442
+ let dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(data));
443
+ let hrefElement = document.createElement('a');
422
444
  document.body.append(hrefElement);
423
445
  hrefElement.download = filename;
424
446
  hrefElement.href = dataStr;
425
447
  hrefElement.click();
426
448
  hrefElement.remove();
427
- }
449
+ };
428
450
 
429
451
  export default {
430
- name: "app",
452
+ name: 'app',
431
453
  components: {
432
- Autocomplete,
433
- Button,
434
- Col,
435
- Icon,
436
- Input,
437
- InputNumber,
438
- Popover,
439
- Row,
440
- Switch,
441
- Upload,
442
- ElIconFolderOpened,
443
- ElIconSetting,
454
+ ElAutocomplete,
455
+ ElButton,
456
+ ElCol,
457
+ ElInput,
458
+ ElInputNumber,
459
+ ElPopover,
460
+ ElRow,
461
+ ElSwitch,
444
462
  DropZone,
445
463
  ScaffoldVuer,
446
464
  ModelsTable,
@@ -449,7 +467,7 @@ export default {
449
467
  provide() {
450
468
  return {
451
469
  $annotator: this.annotator,
452
- }
470
+ };
453
471
  },
454
472
  data: function () {
455
473
  return {
@@ -475,22 +493,21 @@ export default {
475
493
  y_offset: 50,
476
494
  width: 128,
477
495
  height: 128,
478
- align: "top-right",
496
+ align: 'top-right',
479
497
  },
480
- markerLabels: { },
498
+ markerLabels: {},
481
499
  render: true,
482
- region: "",
483
- viewURL: "",
500
+ region: '',
501
+ viewURL: '',
484
502
  renderInfoOn: false,
485
503
  rendererInfo: undefined,
486
- format: "metadata",
504
+ format: 'metadata',
487
505
  sceneSettings: [],
488
- searchInput: "",
489
- searchText: "",
506
+ searchInput: '',
507
+ searchText: '',
490
508
  loadTextureVolumeOnReady: false,
491
509
  readyCallback: undefined,
492
- flatmapAPI: "https://mapcore-demo.org/devel/flatmap/v4/",
493
- helpMode: false,
510
+ flatmapAPI: 'https://mapcore-demo.org/devel/flatmap/v4/',
494
511
  helpModeActiveItem: 0,
495
512
  helpModeLastItem: false,
496
513
  useHelpModeDialog: true,
@@ -501,27 +518,33 @@ export default {
501
518
  ElIconSetting: shallowRef(ElIconSetting),
502
519
  ElIconFolderOpened: shallowRef(ElIconFolderOpened),
503
520
  auto: NaN,
504
- annotator: markRaw(new AnnotationService(`https://mapcore-demo.org/devel/flatmap/v4/annotator`)),
521
+ annotator: markRaw(
522
+ new AnnotationService(`https://mapcore-demo.org/devel/flatmap/v4/annotator`),
523
+ ),
505
524
  fitBoundingBox: false,
506
525
  usageConfig: {
507
526
  tubeLines: false,
508
- showTubeLinesControls: true
527
+ showTubeLinesControls: true,
509
528
  },
510
529
  screenshotData: {
511
- filename: "screenshot",
530
+ filename: 'screenshot',
512
531
  width: 1920,
513
- height: 1080
532
+ height: 1080,
514
533
  },
515
534
  wholeBody: false,
516
535
  rules: {
517
536
  filename: [
518
537
  { required: true, message: 'Please enter a filename', trigger: 'blur' },
519
- { pattern: /^[a-zA-Z0-9_-]+$/, message: 'Filename can only contain letters, numbers, hyphens, and underscores', trigger: 'blur' } ],
520
- width: [ { required: true, message: 'Please specify width', trigger: 'change' } ],
521
- height: [ { required: true, message: 'Please specify height', trigger: 'change' }
522
- ]
523
- }
524
- }
538
+ {
539
+ pattern: /^[a-zA-Z0-9_-]+$/,
540
+ message: 'Filename can only contain letters, numbers, hyphens, and underscores',
541
+ trigger: 'blur',
542
+ },
543
+ ],
544
+ width: [{ required: true, message: 'Please specify width', trigger: 'change' }],
545
+ height: [{ required: true, message: 'Please specify height', trigger: 'change' }],
546
+ },
547
+ };
525
548
  },
526
549
  watch: {
527
550
  input: function () {
@@ -530,30 +553,33 @@ export default {
530
553
  tumbleOn: function () {
531
554
  this.autoTumble();
532
555
  },
533
- markerCluster: function(val) {
556
+ markerCluster: function (val) {
534
557
  if (val) {
535
558
  this.markerLabels = {
536
- "body proper": 9,
537
- "Spinal cord": 8,
538
- "lung": 11,
539
- "stomach": {number:12, imgURL: 'https://mapcore-bucket1.s3.us-west-2.amazonaws.com/texture/arm1/jpg/0984.jpg'},
540
- "urinary bladder": 11,
541
- "Brainstem": 11,
542
- "heart": 9,
543
- "skin epidermis": 5,
544
- "Diaphragm": 7,
545
- "colon": 9,
546
- "vagus nerve": 3,
547
- "myenteric nerve plexus": 2,
548
- "esophagus": 1,
549
- "urethra": 3
559
+ 'body proper': 9,
560
+ 'Spinal cord': 8,
561
+ lung: 11,
562
+ stomach: {
563
+ number: 12,
564
+ imgURL: 'https://mapcore-bucket1.s3.us-west-2.amazonaws.com/texture/arm1/jpg/0984.jpg',
565
+ },
566
+ 'urinary bladder': 11,
567
+ Brainstem: 11,
568
+ heart: 9,
569
+ 'skin epidermis': 5,
570
+ Diaphragm: 7,
571
+ colon: 9,
572
+ 'vagus nerve': 3,
573
+ 'myenteric nerve plexus': 2,
574
+ esophagus: 1,
575
+ urethra: 3,
550
576
  };
551
577
  } else {
552
- this.markerLabels = { };
578
+ this.markerLabels = {};
553
579
  }
554
580
  },
555
- "route.query": {
556
- handler: "parseQuery",
581
+ 'route.query': {
582
+ handler: 'parseQuery',
557
583
  deep: true,
558
584
  immediate: true,
559
585
  },
@@ -587,9 +613,9 @@ export default {
587
613
  },
588
614
  exportGLB: function () {
589
615
  this.$refs.scaffold.exportGLTF(true).then((data) => {
590
- let blob = new Blob([data], { type: "octet/stream" });
616
+ let blob = new Blob([data], { type: 'octet/stream' });
591
617
  let url = window.URL.createObjectURL(blob);
592
- let hrefElement = document.createElement("a");
618
+ let hrefElement = document.createElement('a');
593
619
  document.body.append(hrefElement);
594
620
  hrefElement.download = `export.glb`;
595
621
  hrefElement.href = url;
@@ -597,31 +623,31 @@ export default {
597
623
  hrefElement.remove();
598
624
  });
599
625
  },
600
- exportOfflineAnnotations: function() {
626
+ exportOfflineAnnotations: function () {
601
627
  const annotations = this.$refs.scaffold.getOfflineAnnotations();
602
628
  const filename = 'scaffoldAnnotations' + JSON.stringify(new Date()) + '.json';
603
629
  writeTextFile(filename, annotations);
604
630
  },
605
- onReaderLoad: function(event) {
631
+ onReaderLoad: function (event) {
606
632
  const annotationsList = JSON.parse(event.target.result);
607
633
  this.$refs.scaffold.importOfflineAnnotations(annotationsList);
608
634
  },
609
- importOfflineAnnotations: function() {
610
- const selectedFile = document.getElementById("annotations-upload").files[0];
635
+ importOfflineAnnotations: function () {
636
+ const selectedFile = document.getElementById('annotations-upload').files[0];
611
637
  const reader = new FileReader();
612
638
  reader.onload = this.onReaderLoad;
613
639
  reader.readAsText(selectedFile);
614
640
  },
615
641
  objectAdded: function (zincObject) {
616
642
  if (this.consoleOn) {
617
- console.log(zincObject)
618
- console.log(this.$refs.scaffold.$module.scene.getBoundingBox())
643
+ console.log(zincObject);
644
+ console.log(this.$refs.scaffold.$module.scene.getBoundingBox());
619
645
  }
620
646
  if (zincObject.isGeometry) {
621
647
  zincObject._lod._material.wireframe = this.wireframe;
622
648
  }
623
649
  if (this.wholeBody) {
624
- if (zincObject.getRegion().getName() === "Nerves") {
650
+ if (zincObject.getRegion().getName() === 'Nerves') {
625
651
  zincObject.setGreyScale(true);
626
652
  }
627
653
  }
@@ -639,9 +665,9 @@ export default {
639
665
  //[-100, 0, 0, 0, 0, -100, 0, 0, 0, 0, -100, 0, -60, -100, 30, 1]
640
666
  if (overlap) {
641
667
  const url =
642
- "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/WholeBody/6-match-2023/human/nerve_metadata.json";
668
+ 'https://mapcore-bucket1.s3.us-west-2.amazonaws.com/WholeBody/6-match-2023/human/nerve_metadata.json';
643
669
  if (this.route.query.url !== encodeURI(url)) {
644
- this.router.replace({ path: "/", query: { url } });
670
+ this.router.replace({ path: '/', query: { url } });
645
671
  this.readyCallback = testVolume;
646
672
  return;
647
673
  } else {
@@ -656,9 +682,9 @@ export default {
656
682
  //Test texture
657
683
  if (overlap) {
658
684
  const url =
659
- "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/WholeBody/6-match-2023/human/nerve_metadata.json";
685
+ 'https://mapcore-bucket1.s3.us-west-2.amazonaws.com/WholeBody/6-match-2023/human/nerve_metadata.json';
660
686
  if (this.route.query.url !== encodeURI(url)) {
661
- this.router.replace({ path: "/", query: { url } });
687
+ this.router.replace({ path: '/', query: { url } });
662
688
  this.readyCallback = testSlides;
663
689
  return;
664
690
  } else {
@@ -673,9 +699,9 @@ export default {
673
699
  //Test texture
674
700
  if (overlap) {
675
701
  const url =
676
- "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/texture/arm1/arm_texture_metadata.json";
702
+ 'https://mapcore-bucket1.s3.us-west-2.amazonaws.com/texture/arm1/arm_texture_metadata.json';
677
703
  if (this.route.query.url !== encodeURI(url)) {
678
- this.router.replace({ path: "/", query: { url } });
704
+ this.router.replace({ path: '/', query: { url } });
679
705
  this.readyCallback = testArmSlides;
680
706
  return;
681
707
  } else {
@@ -688,66 +714,73 @@ export default {
688
714
  },
689
715
  multipleNifti: async function () {
690
716
  const v1 = {
691
- "id": "mesh-location-orientation",
692
- "locations": [
717
+ id: 'mesh-location-orientation',
718
+ locations: [
693
719
  {
694
- "identifier": 1,
695
- "label": "original",
696
- "orientation": [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
697
- "position": [-283, -363, 1090],
698
- "scale": [540, 540, 276],
699
- "flipY": false,
700
- "flipZ": true,
701
- "reference_point": "corner"
702
- }
720
+ identifier: 1,
721
+ label: 'original',
722
+ orientation: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
723
+ position: [-283, -363, 1090],
724
+ scale: [540, 540, 276],
725
+ flipY: false,
726
+ flipZ: true,
727
+ reference_point: 'corner',
728
+ },
703
729
  ],
704
- "settings": {
705
- "slides": [
730
+ settings: {
731
+ slides: [
706
732
  {
707
- "direction": "x",
708
- "value": 0.5
733
+ direction: 'x',
734
+ value: 0.5,
709
735
  },
710
736
  {
711
- "direction": "y",
712
- "value": 0.5
737
+ direction: 'y',
738
+ value: 0.5,
713
739
  },
714
740
  {
715
- "direction": "z",
716
- "value": 0.45
717
- }
718
- ]
741
+ direction: 'z',
742
+ value: 0.45,
743
+ },
744
+ ],
719
745
  },
720
- "type": "slides"
721
- }
746
+ type: 'slides',
747
+ };
722
748
  const options = {
723
749
  hideWhitePixel: false,
724
750
  hideBlackPixel: false,
725
751
  keepScalePosition: true,
726
752
  filterByValue: true,
727
753
  timeEnabled: true,
728
- }
754
+ };
729
755
  const urls = [
730
- "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/digital_twins/080626-demo/downsample_phase_1.nii.gz",
731
- "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/digital_twins/080626-demo/downsample_phase_3.nii.gz",
732
- "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/digital_twins/080626-demo/downsample_phase_5.nii.gz"
733
- ]
734
- const newTexture = await this.$refs.scaffold.readNIFTIFromSource(urls, true, undefined, v1, options, true);
756
+ 'https://mapcore-bucket1.s3.us-west-2.amazonaws.com/digital_twins/080626-demo/downsample_phase_1.nii.gz',
757
+ 'https://mapcore-bucket1.s3.us-west-2.amazonaws.com/digital_twins/080626-demo/downsample_phase_3.nii.gz',
758
+ 'https://mapcore-bucket1.s3.us-west-2.amazonaws.com/digital_twins/080626-demo/downsample_phase_5.nii.gz',
759
+ ];
760
+ const newTexture = await this.$refs.scaffold.readNIFTIFromSource(
761
+ urls,
762
+ true,
763
+ undefined,
764
+ v1,
765
+ options,
766
+ true,
767
+ );
735
768
  newTexture.timeEnabled = true;
736
769
  newTexture.setIsPickable(false);
737
770
  const scene = this.$refs.scaffold.$module.scene;
738
771
  newTexture.setBrightness(0.38);
739
772
  newTexture.setContrast(3.5);
740
- newTexture.setPosition(-158.73, -150.51, -198.50);
773
+ newTexture.setPosition(-158.73, -150.51, -198.5);
741
774
  scene.addZincObject(newTexture);
742
775
  },
743
776
  saveSettings: function () {
744
777
  this.sceneSettings.push(this.$refs.scaffold.getState());
745
778
  },
746
779
  restoreSettings: function () {
747
- if (this.sceneSettings.length > 0)
748
- this.$refs.scaffold.setState(this.sceneSettings.pop());
780
+ if (this.sceneSettings.length > 0) this.$refs.scaffold.setState(this.sceneSettings.pop());
749
781
  },
750
782
  viewModelClicked: function (location) {
783
+ this.format = 'metadata';
751
784
  this.input = location;
752
785
  },
753
786
  userPrimitivesUpdated: function (event) {
@@ -770,17 +803,16 @@ export default {
770
803
  search: function (term) {
771
804
  this.$refs.scaffold.search(term, true);
772
805
  },
773
- viewerMounted: function() {
774
- // this.selectedCoordinates =
775
- // this.$refs.scaffold.getDynamicSelectedCoordinates();
776
- // this.rendererInfo = this.$refs.scaffold.getRendererInfo();
806
+ viewerMounted: function () {
807
+ // this.selectedCoordinates = this.$refs.scaffold.getDynamicSelectedCoordinates();
808
+ // this.rendererInfo = this.$refs.scaffold.getRendererInfo();
777
809
  },
778
- PrintViewport: function() {
810
+ PrintViewport: function () {
779
811
  const scene = this.$refs.scaffold.$module.scene;
780
812
  console.log(scene.getZincCameraControls().getCurrentViewport());
781
813
  },
782
814
  fetchSuggestions: function (term, cb) {
783
- if (term === "" || !this.$refs.scaffold) {
815
+ if (term === '' || !this.$refs.scaffold) {
784
816
  cb([]);
785
817
  }
786
818
  cb(
@@ -788,21 +820,17 @@ export default {
788
820
  const value = item.suggestion;
789
821
  return {
790
822
  value: value,
791
- label: value
823
+ label: value,
792
824
  };
793
- })
825
+ }),
794
826
  );
795
827
  if (this.consoleOn) {
796
- console.log(
797
- "found suggestions",
798
- this.$refs.scaffold.fetchSuggestions(term)
799
- );
828
+ console.log('found suggestions', this.$refs.scaffold.fetchSuggestions(term));
800
829
  }
801
830
  },
802
831
  autoTumble: function () {
803
832
  const flag = this.tumbleOn;
804
- let cameracontrol =
805
- this.$refs.scaffold.$module.scene.getZincCameraControls();
833
+ let cameracontrol = this.$refs.scaffold.$module.scene.getZincCameraControls();
806
834
  if (flag) {
807
835
  this.displayUI = false;
808
836
  cameracontrol.enableAutoTumble();
@@ -822,8 +850,8 @@ export default {
822
850
  }
823
851
  });
824
852
  },
825
- onError: function(payload) {
826
- if (payload?.type === "download-error") {
853
+ onError: function (payload) {
854
+ if (payload?.type === 'download-error') {
827
855
  const dropZone = this.$refs.dropzone;
828
856
  if (dropZone) {
829
857
  const realFilename = dropZone.findRealFilename(payload.xhr.responseURL);
@@ -834,13 +862,13 @@ export default {
834
862
  }
835
863
  },
836
864
  onReady: function () {
837
- if (this.consoleOn) console.log(this.$refs.scaffold)
865
+ if (this.consoleOn) console.log(this.$refs.scaffold);
838
866
  if (this.readyCallback) {
839
867
  this.readyCallback(this.$refs.scaffold, texture_prefix);
840
868
  this.readyCallback = undefined;
841
869
  } else {
842
870
  const url =
843
- "https://mapcore-bucket1.s3.us-west-2.amazonaws.com/texture/arm1/arm_texture_metadata.json";
871
+ 'https://mapcore-bucket1.s3.us-west-2.amazonaws.com/texture/arm1/arm_texture_metadata.json';
844
872
  if (this.route.query.url === encodeURI(url)) {
845
873
  testArmSlides(this.$refs.scaffold);
846
874
  }
@@ -856,8 +884,8 @@ export default {
856
884
  coord[2] + normal.z * 1000,
857
885
  ];
858
886
  const returned = this.$refs.scaffold.$module.scene.createLines(
859
- "test",
860
- "lines",
887
+ 'test',
888
+ 'lines',
861
889
  [newCoords, coord],
862
890
  0x00ee22,
863
891
  );
@@ -868,13 +896,20 @@ export default {
868
896
  onSelected: function (data) {
869
897
  if (data && data.length > 0 && data[0].data.group) {
870
898
  if (this.consoleOn) console.log(data[0], data[0].extraData.intersected);
871
- if (this.createLinesWithNormal && data[0].extraData.worldCoords &&
872
- data[0].extraData.intersected?.face) {
873
- this.addLinesWithNormal(data[0].extraData.worldCoords, data[0].extraData.intersected.face.normal)
899
+ if (
900
+ this.createLinesWithNormal &&
901
+ data[0].extraData.worldCoords &&
902
+ data[0].extraData.intersected?.face
903
+ ) {
904
+ this.addLinesWithNormal(
905
+ data[0].extraData.worldCoords,
906
+ data[0].extraData.intersected.face.normal,
907
+ );
874
908
  }
875
- delete this.route.query["viewURL"];
909
+ delete this.route.query['viewURL'];
876
910
  //this.$refs.scaffold.showRegionTooltipWithAnnotations(data, false, true);
877
- if (this.onClickMarkers) this.$refs.scaffold.setMarkerModeForObjectsWithName(data[0].data.group, "on");
911
+ if (this.onClickMarkers)
912
+ this.$refs.scaffold.setMarkerModeForObjectsWithName(data[0].data.group, 'on');
878
913
  }
879
914
  if (this.consoleOn) console.log(data);
880
915
  },
@@ -885,16 +920,16 @@ export default {
885
920
  this.pos[1] = data.target[1];
886
921
  },
887
922
  onFilesDrop: function (payload) {
888
- if (payload.format == "gltf") this.format = "gltf";
889
- else this.format = "metadata";
923
+ if (payload.format == 'gltf') this.format = 'gltf';
924
+ else this.format = 'metadata';
890
925
  this.input = payload.url;
891
926
  },
892
927
  parseInput: function () {
893
928
  if (this.route.query.url !== this.input) {
894
929
  const queries = { ...this.route.query };
895
- if (this.input && this.input !== "") queries.url = this.input;
930
+ if (this.input && this.input !== '') queries.url = this.input;
896
931
  this.router.replace({
897
- path: "/",
932
+ path: '/',
898
933
  query: { ...this.route.query, url: this.input },
899
934
  });
900
935
  }
@@ -911,31 +946,31 @@ export default {
911
946
  this.url = query.url;
912
947
  } else {
913
948
  this.url =
914
- "https://mapcore-bucket1.s3-us-west-2.amazonaws.com/others/29_Jan_2020/heartICN_metadata.json";
949
+ 'https://mapcore-bucket1.s3-us-west-2.amazonaws.com/others/29_Jan_2020/heartICN_metadata.json';
915
950
  }
916
- if (this.url.includes(".gltf") || this.url.includes(".glb")) {
917
- this.format = "gltf";
918
- } else if (this.url.includes(".json")) {
919
- this.format = "metadata";
951
+ if (this.url.includes('.gltf') || this.url.includes('.glb')) {
952
+ this.format = 'gltf';
953
+ } else if (this.url.includes('.json')) {
954
+ this.format = 'metadata';
920
955
  }
921
956
  this.input = this.url;
922
957
  if (query.region) {
923
958
  this.region = query.region;
924
959
  } else {
925
- this.region = "";
960
+ this.region = '';
926
961
  }
927
962
  if (query.viewURL) {
928
963
  this.viewURL = query.viewURL;
929
964
  } else {
930
- this.viewURL = "";
965
+ this.viewURL = '';
931
966
  }
932
967
 
933
- if (query.wholeBody && query.wholeBody === "true") {
934
- console.log("wholebody", query.wholeBody)
968
+ if (query.wholeBody && query.wholeBody === 'true') {
969
+ console.log('wholebody', query.wholeBody);
935
970
  this.wholeBody = true;
936
971
  this.usageConfig.tubeLines = true;
937
972
  }
938
- })
973
+ });
939
974
  },
940
975
  onHelpModeShowNext: function () {
941
976
  this.helpModeActiveItem += 1;
@@ -966,9 +1001,8 @@ export default {
966
1001
  </script>
967
1002
 
968
1003
  <style lang="scss">
969
-
970
1004
  #app {
971
- font-family: "Asap", sans-serif;
1005
+ font-family: 'Asap', sans-serif;
972
1006
  -webkit-font-smoothing: antialiased;
973
1007
  -moz-osx-font-smoothing: grayscale;
974
1008
  text-align: center;
@@ -1083,8 +1117,7 @@ svg.map-icon {
1083
1117
  z-index: 2;
1084
1118
  }
1085
1119
 
1086
- input[type="file"] {
1120
+ input[type='file'] {
1087
1121
  display: none;
1088
1122
  }
1089
-
1090
1123
  </style>