@abi-software/scaffoldvuer 1.2.1 → 1.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/scaffoldvuer",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -41,7 +41,7 @@
41
41
  "*.js"
42
42
  ],
43
43
  "dependencies": {
44
- "@abi-software/flatmapvuer": "^1.1.0",
44
+ "@abi-software/map-utilities": "^1.0.0",
45
45
  "@abi-software/sparc-annotation": "^0.3.1",
46
46
  "@abi-software/svg-sprite": "^1.0.0",
47
47
  "@element-plus/icons-vue": "^2.3.1",
@@ -55,7 +55,7 @@
55
55
  "vue": "^3.4.15",
56
56
  "vue-router": "^4.2.5",
57
57
  "vue3-component-svg-sprite": "^0.0.1",
58
- "zincjs": "^1.8.3"
58
+ "zincjs": "^1.9.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@vitejs/plugin-vue": "^4.6.2",
package/src/App.vue CHANGED
@@ -27,10 +27,12 @@
27
27
  :view-u-r-l="viewURL"
28
28
  :format="format"
29
29
  :marker-labels="markerLabels"
30
+ :enableLocalAnnotations="false"
30
31
  @open-map="openMap"
31
32
  @on-ready="onReady"
32
33
  @scaffold-selected="onSelected"
33
34
  @scaffold-navigated="onNavigated"
35
+ @user-primitives-updated="userPrimitivesUpdated"
34
36
  @timeChanged="updateCurrentTime"
35
37
  @zinc-object-added="objectAdded"
36
38
  @vue:mounted="viewerMounted"
@@ -64,80 +66,96 @@
64
66
  </el-row>
65
67
 
66
68
  <el-row :gutter="20" justify="center" align="middle">
67
- <el-col span="auto">
69
+ <el-col :span="auto">
68
70
  <el-switch v-model="displayUI" active-text="UI" />
69
71
  </el-col>
70
- <el-col span="auto">
72
+ <el-col :span="auto">
71
73
  <el-switch v-model="displayMarkers" active-text="Markers" active-icon-class="el-icon-location"
72
74
  active-color="#8300bf" />
73
75
  </el-col>
74
- <el-col span="auto">
76
+ <el-col :span="auto">
75
77
  <el-switch v-model="markerCluster" active-text="Marker Cluster" active-icon-class="el-icon-location"
76
78
  active-color="#8300bf" />
77
79
  </el-col>
78
- <el-col span="auto">
80
+ <el-col :span="auto">
79
81
  <el-switch v-model="displayMinimap" active-text="Minimap" active-icon-class="el-icon-discover"
80
82
  active-color="#8300bf" />
81
83
  </el-col>
82
84
  </el-row>
83
85
 
84
86
  <el-row :gutter="20" justify="center" align="middle">
85
- <el-col span="auto">
87
+ <el-col :span="auto">
86
88
  <el-switch v-model="tumbleOn" active-text="Tumble" active-color="#8300bf" />
87
89
  </el-col>
88
- <el-col span="auto">
90
+ <el-col :span="auto">
89
91
  <el-row>
90
92
  <el-col :span="8"> x: </el-col>
91
93
  <el-col :span="16">
92
94
  <el-input-number class="tumble-direction" controls-position="right" v-model="tumbleDirection[0]" :min="-1.0"
93
- :max="1.0" :controls="false" placeholder="Please input" label="x" @change="autoTumble" />
95
+ :max="1.0" :controls="false" placeholder="Please input" aria-label="x" @change="autoTumble" />
94
96
  </el-col>
95
97
  </el-row>
96
98
  </el-col>
97
- <el-col span="auto">
99
+ <el-col :span="auto">
98
100
  <el-row>
99
101
  <el-col :span="8"> y: </el-col>
100
102
  <el-col :span="16">
101
103
  <el-input-number class="tumble-direction" controls-position="right" v-model="tumbleDirection[1]" :min="-1.0"
102
- :max="1.0" :controls="false" placeholder="Please input" label="y" @change="autoTumble" />
104
+ :max="1.0" :controls="false" placeholder="Please input" aria-label="y" @change="autoTumble" />
103
105
  </el-col>
104
106
  </el-row>
105
107
  </el-col>
106
108
  </el-row>
107
109
 
108
110
  <el-row :gutter="20" justify="center" align="middle">
109
- <el-col span="auto">
111
+ <el-col :span="auto">
110
112
  <el-button size="small" @click="helpMode = !helpMode">
111
113
  Help Mode
112
114
  </el-button>
113
115
  </el-col>
114
- <el-col span="auto">
116
+ <el-col :span="auto">
115
117
  <el-button size="small" @click="screenCapture()"> Capture </el-button>
116
118
  </el-col>
117
- <el-col span="auto">
118
- <el-button size="small" @click="changeMarkers"> Change Markers </el-button>
119
- </el-col>
120
119
  </el-row>
121
120
 
122
121
  <el-row :gutter="20" justify="center" align="middle">
123
- <el-col span="auto">
122
+ <el-col :span="auto">
124
123
  <el-button size="small" @click="saveSettings()">
125
124
  Save Settings
126
125
  </el-button>
127
126
  </el-col>
128
- <el-col span="auto">
127
+ <el-col :span="auto">
129
128
  <el-button size="small" @click="restoreSettings()">
130
129
  Restore Settings
131
130
  </el-button>
132
131
  </el-col>
133
- <el-col span="auto">
132
+ <el-col :span="auto">
134
133
  <el-button size="small" @click="exportGLB()"> Export GLB </el-button>
135
134
  </el-col>
136
- <el-col span="auto">
135
+ <el-col :span="auto">
137
136
  <el-button size="small" @click="exportGLTF()"> Export GLTF </el-button>
138
137
  </el-col>
139
138
  </el-row>
140
139
 
140
+ <el-row :gutter="20" justify="center" align="middle">
141
+ <el-col :span="auto">
142
+ <el-button size="small" @click="exportLocalAnnotations()">
143
+ Export Annotations
144
+ </el-button>
145
+ </el-col>
146
+ <el-col :span="auto">
147
+ <el-button size="small">
148
+ <label for="annotations-upload">Import Annotations</label>
149
+ <input
150
+ id="annotations-upload"
151
+ type="file"
152
+ accept="application/json"
153
+ @change="importLocalAnnotations"
154
+ />
155
+ </el-button>
156
+ </el-col>
157
+ </el-row>
158
+
141
159
  <el-row justify="center" align="middle">
142
160
  <el-col>
143
161
  <el-row :gutter="20" justify="center" align="middle">
@@ -147,35 +165,35 @@
147
165
  </el-row>
148
166
  <el-row :gutter="20" justify="center" align="middle" v-if="syncMode">
149
167
  <el-col :span="8">
150
- <el-input-number v-model="zoom" :min="1.0" :controls="false" placeholder="Please input" label="zoom" />
168
+ <el-input-number v-model="zoom" :min="1.0" :controls="false" placeholder="Please input" aria-label="zoom" />
151
169
  </el-col>
152
170
  <el-col :span="8">
153
- <el-input-number v-model="pos[0]" :min="-1.0" :max="1.0" :controls="false" placeholder="Please input" label="x" />
171
+ <el-input-number v-model="pos[0]" :min="-1.0" :max="1.0" :controls="false" placeholder="Please input" aria-label="x" />
154
172
  </el-col>
155
173
  <el-col :span="8">
156
- <el-input-number v-model="pos[1]" :min="-1.0" :max="1.0" :controls="false" label="y" />
174
+ <el-input-number v-model="pos[1]" :min="-1.0" :max="1.0" :controls="false" aria-label="y" />
157
175
  </el-col>
158
176
  </el-row>
159
177
  </el-col>
160
178
  </el-row>
161
179
 
162
180
  <el-row :gutter="20" justify="center" align="middle">
163
- <el-col span="auto">
181
+ <el-col :span="auto">
164
182
  <el-switch v-model="render" active-text="Rendering" active-color="#8300bf" />
165
183
  </el-col>
166
- <el-col span="auto">
184
+ <el-col :span="auto">
167
185
  <el-switch v-model="renderInfoOn" active-text="Renderer Info" active-color="#8300bf" />
168
186
  </el-col>
169
187
  </el-row>
170
188
 
171
189
  <template v-if="renderInfoOn && rendererInfo">
172
190
  <el-row :gutter="20" justify="center" align="middle">
173
- <el-col v-for="(value, name) in rendererInfo.memory" :key="name" span="auto">
191
+ <el-col v-for="(value, name) in rendererInfo.memory" :key="name" :span="auto">
174
192
  {{ name }} : {{ value }}
175
193
  </el-col>
176
194
  </el-row>
177
195
  <el-row :gutter="20" justify="center" align="middle">
178
- <el-col v-for="(value, name) in rendererInfo.render" :key="name" span="auto">
196
+ <el-col v-for="(value, name) in rendererInfo.render" :key="name" :span="auto">
179
197
  {{ name }} : {{ value }}
180
198
  </el-col>
181
199
  </el-row>
@@ -189,41 +207,41 @@
189
207
  </el-col>
190
208
  </el-row>
191
209
  <el-row :gutter="20" justify="center" align="middle">
192
- <el-col span="auto">
210
+ <el-col :span="auto">
193
211
  <el-button size="small" @click="featureTextureVolume(false)">
194
212
  Texture volume
195
213
  </el-button>
196
214
  </el-col>
197
- <el-col span="auto">
215
+ <el-col :span="auto">
198
216
  <el-button size="small" @click="featureTextureSlides(false)">
199
217
  Texture slides
200
218
  </el-button>
201
219
  </el-col>
202
- <el-col span="auto">
220
+ <el-col :span="auto">
203
221
  <el-button size="small" @click="featureTextureVolume(true)">
204
222
  Body volume
205
223
  </el-button>
206
224
  </el-col>
207
- <el-col span="auto">
225
+ <el-col :span="auto">
208
226
  <el-button size="small" @click="featureTextureSlides(true)">
209
227
  Body slides
210
228
  </el-button>
211
229
  </el-col>
212
- <el-col span="auto">
230
+ <el-col :span="auto">
213
231
  <el-button size="small" @click="featureArmSlides(true)">
214
232
  Arm slides
215
233
  </el-button>
216
234
  </el-col>
217
235
  </el-row>
218
236
  <el-row :gutter="20" justify="center" align="middle">
219
- <el-col span="auto">
237
+ <el-col :span="auto">
220
238
  <el-switch
221
239
  v-model="onClickMarkers"
222
240
  active-text="Markers On Selection"
223
241
  active-color="#8300bf"
224
242
  />
225
243
  </el-col>
226
- <el-col span="auto">
244
+ <el-col :span="auto">
227
245
  <el-switch
228
246
  v-model="wireframe"
229
247
  active-text="Wireframe"
@@ -297,13 +315,27 @@ import {
297
315
  ElInputNumber as InputNumber,
298
316
  ElPopover as Popover,
299
317
  ElRow as Row,
318
+ ElUpload as Upload,
300
319
  ElSwitch as Switch,
301
320
  } from "element-plus";
302
321
  import { useRoute, useRouter } from 'vue-router'
303
- import HelpModeDialog from './components/HelpModeDialog.vue';
322
+ import { HelpModeDialog } from '@abi-software/map-utilities'
323
+ import '@abi-software/map-utilities/dist/style.css'
304
324
 
305
325
  let texture_prefix = undefined;
306
326
 
327
+ const writeTextFile = (filename, data) => {
328
+ let dataStr =
329
+ "data:text/json;charset=utf-8," +
330
+ encodeURIComponent(JSON.stringify(data));
331
+ let hrefElement = document.createElement("a");
332
+ document.body.append(hrefElement);
333
+ hrefElement.download = filename;
334
+ hrefElement.href = dataStr;
335
+ hrefElement.click();
336
+ hrefElement.remove();
337
+ }
338
+
307
339
  export default {
308
340
  name: "app",
309
341
  components: {
@@ -316,6 +348,7 @@ export default {
316
348
  Popover,
317
349
  Row,
318
350
  Switch,
351
+ Upload,
319
352
  ElIconFolderOpened,
320
353
  ElIconSetting,
321
354
  DropZone,
@@ -326,7 +359,7 @@ export default {
326
359
  data: function () {
327
360
  return {
328
361
  consoleOn: true,
329
- createPoints: false,
362
+ createLinesWithNormal: false,
330
363
  url: undefined,
331
364
  input: undefined,
332
365
  displayUI: true,
@@ -341,7 +374,7 @@ export default {
341
374
  tumbleOn: false,
342
375
  tumbleDirection: [1.0, 0.0],
343
376
  showColourPicker: true,
344
- markerCluster: true,
377
+ markerCluster: false,
345
378
  minimapSettings: {
346
379
  x_offset: 16,
347
380
  y_offset: 50,
@@ -349,22 +382,7 @@ export default {
349
382
  height: 128,
350
383
  align: "top-right",
351
384
  },
352
- markerLabels: {
353
- "body proper": 9,
354
- "Spinal cord": 8,
355
- "lung": 11,
356
- "stomach": 12,
357
- "urinary bladder": 11,
358
- "Brainstem": 11,
359
- "heart": 9,
360
- "skin epidermis": 5,
361
- "Diaphragm": 7,
362
- "colon": 9,
363
- "vagus nerve": 3,
364
- "myenteric nerve plexus": 2,
365
- "esophagus": 1,
366
- "urethra": 3
367
- },
385
+ markerLabels: { },
368
386
  render: true,
369
387
  region: "",
370
388
  viewURL: "",
@@ -388,7 +406,7 @@ export default {
388
406
  router: useRouter(),
389
407
  ElIconSetting: shallowRef(ElIconSetting),
390
408
  ElIconFolderOpened: shallowRef(ElIconFolderOpened),
391
- coordinatesClicked: [],
409
+ auto: NaN
392
410
  };
393
411
  },
394
412
  watch: {
@@ -398,6 +416,28 @@ export default {
398
416
  tumbleOn: function () {
399
417
  this.autoTumble();
400
418
  },
419
+ markerCluster: function(val) {
420
+ if (val) {
421
+ this.markerLabels = {
422
+ "body proper": 9,
423
+ "Spinal cord": 8,
424
+ "lung": 11,
425
+ "stomach": 12,
426
+ "urinary bladder": 11,
427
+ "Brainstem": 11,
428
+ "heart": 9,
429
+ "skin epidermis": 5,
430
+ "Diaphragm": 7,
431
+ "colon": 9,
432
+ "vagus nerve": 3,
433
+ "myenteric nerve plexus": 2,
434
+ "esophagus": 1,
435
+ "urethra": 3
436
+ };
437
+ } else {
438
+ this.markerLabels = { };
439
+ }
440
+ },
401
441
  "route.query": {
402
442
  handler: "parseQuery",
403
443
  deep: true,
@@ -424,15 +464,8 @@ export default {
424
464
  methods: {
425
465
  exportGLTF: function () {
426
466
  this.$refs.scaffold.exportGLTF(false).then((data) => {
427
- let dataStr =
428
- "data:text/json;charset=utf-8," +
429
- encodeURIComponent(JSON.stringify(data));
430
- let hrefElement = document.createElement("a");
431
- document.body.append(hrefElement);
432
- hrefElement.download = `export.gltf`;
433
- hrefElement.href = dataStr;
434
- hrefElement.click();
435
- hrefElement.remove();
467
+ const filename = 'export' + JSON.stringify(new Date()) + '.gltf';
468
+ writeTextFile(filename, data);
436
469
  });
437
470
  },
438
471
  exportGLB: function () {
@@ -447,14 +480,26 @@ export default {
447
480
  hrefElement.remove();
448
481
  });
449
482
  },
483
+ exportLocalAnnotations: function() {
484
+ const annotations = this.$refs.scaffold.getLocalAnnotations();
485
+ const filename = 'scaffoldAnnotations' + JSON.stringify(new Date()) + '.json';
486
+ writeTextFile(filename, annotations);
487
+ },
488
+ onReaderLoad: function(event) {
489
+ const annotationsList = JSON.parse(event.target.result);
490
+ this.$refs.scaffold.importLocalAnnotations(annotationsList);
491
+ },
492
+ importLocalAnnotations: function() {
493
+ const selectedFile = document.getElementById("annotations-upload").files[0];
494
+ const reader = new FileReader();
495
+ reader.onload = this.onReaderLoad;
496
+ reader.readAsText(selectedFile);
497
+ },
450
498
  objectAdded: function (zincObject) {
451
499
  if (this.consoleOn) {
452
500
  console.log(zincObject)
453
501
  console.log(this.$refs.scaffold.$module.scene.getBoundingBox())
454
502
  }
455
- if (this._objects.length === 0) {
456
- zincObject.setMarkerMode("on");
457
- }
458
503
  if (zincObject.isGeometry) {
459
504
  zincObject._lod._material.wireframe = this.wireframe;
460
505
  }
@@ -528,6 +573,9 @@ export default {
528
573
  viewModelClicked: function (location) {
529
574
  this.input = location;
530
575
  },
576
+ userPrimitivesUpdated: function (event) {
577
+ console.log(event);
578
+ },
531
579
  screenCapture: function () {
532
580
  this.$refs.scaffold.captureScreenshot("capture.png");
533
581
  },
@@ -599,40 +647,37 @@ export default {
599
647
  }
600
648
  this.scaffoldRef = this.$refs.scaffold;
601
649
  },
602
- addLines: function (coord) {
603
- if (this.coordinatesClicked.length === 1) {
650
+ addLinesWithNormal: function (coord, normal) {
651
+ if (coord && normal) {
652
+ const newCoords = [
653
+ coord[0] + normal.x * 1000,
654
+ coord[1] + normal.y * 1000,
655
+ coord[2] + normal.z * 1000,
656
+ ];
604
657
  const returned = this.$refs.scaffold.$module.scene.createLines(
605
- "test",
606
- "lines",
607
- [this.coordinatesClicked[0], coord],
608
- 0x00ee22,
609
- );
610
- this.coordinatesClicked.length = 0;
611
- if (this.consoleOn) console.log(returned);
612
- } else {
613
- this.coordinatesClicked.push(coord);
658
+ "test",
659
+ "lines",
660
+ [newCoords, coord],
661
+ 0x00ee22,
662
+ );
663
+ returned.zincObject.isEditable = true;
664
+ if (this.consoleOn) console.log(returned);
614
665
  }
615
666
  },
616
667
  onSelected: function (data) {
617
668
  if (data && data.length > 0 && data[0].data.group) {
618
- if (this.consoleOn) console.log(data[0]);
619
- if (this.createPoints && data[0].extraData.worldCoords) {
620
- const returned = this.$refs.scaffold.$module.scene.createPoints(
621
- "test",
622
- "points",
623
- [data[0].extraData.worldCoords],
624
- undefined,
625
- 0x0022ee,
626
- );
669
+ if (this.consoleOn) console.log(data[0], data[0].extraData.intersected);
670
+ if (this.createLinesWithNormal && data[0].extraData.worldCoords &&
671
+ data[0].extraData.intersected?.face) {
672
+ this.addLinesWithNormal(data[0].extraData.worldCoords, data[0].extraData.intersected.face.normal)
627
673
  }
628
674
  delete this.route.query["viewURL"];
629
- this.$refs.scaffold.showRegionTooltipWithAnnotations(data, false, true);
675
+ //this.$refs.scaffold.showRegionTooltipWithAnnotations(data, false, true);
630
676
  if (this.onClickMarkers) this.$refs.scaffold.setMarkerModeForObjectsWithName(data[0].data.group, "on");
631
677
  }
678
+ if (this.consoleOn) console.log(data);
632
679
  },
633
- changeMarkers: function () {
634
- this.markerLabels = {"left atrium": 3, "epicardium": 4 , "stomach": 5};
635
- },
680
+
636
681
  onNavigated: function (data) {
637
682
  this.zoom = data.zoom;
638
683
  this.pos[0] = data.target[0];
@@ -826,4 +871,9 @@ body {
826
871
  svg.map-icon {
827
872
  color: $app-primary-color;
828
873
  }
874
+
875
+ input[type="file"] {
876
+ display: none;
877
+ }
878
+
829
879
  </style>