@abi-software/scaffoldvuer 1.3.3 → 1.4.0-beta.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/dist/scaffoldvuer.js +15555 -15478
- package/dist/scaffoldvuer.umd.cjs +188 -183
- package/dist/style.css +1 -1
- package/package.json +4 -4
- package/src/App.vue +13 -2
- package/src/components/LinesControls.vue +15 -17
- package/src/components/PointsControls.vue +12 -14
- package/src/components/PrimitiveControls.vue +4 -3
- package/src/components/ScaffoldTooltip.vue +36 -12
- package/src/components/ScaffoldTreeControls.vue +22 -19
- package/src/components/ScaffoldVuer.vue +235 -39
- package/src/components/TextureSlidesControls.vue +9 -10
- package/src/components/TransformationControls.vue +7 -8
- package/src/components.d.ts +2 -0
- package/src/main.js +1 -1
- package/src/mixins/imageMixin.js +89 -0
- package/src/services/scicrunchQueries.js +280 -0
- package/src/stores/settings.js +46 -0
- /package/src/{store → stores}/index.js +0 -0
|
@@ -15,9 +15,12 @@
|
|
|
15
15
|
:x="tData.x"
|
|
16
16
|
:y="tData.y"
|
|
17
17
|
:annotationDisplay="annotationDisplay"
|
|
18
|
+
:imageThumbnailSidebar="imageThumbnailSidebar"
|
|
19
|
+
:imageThumbnails="imageThumbnailsEntry"
|
|
18
20
|
@confirm-create="confirmCreate($event)"
|
|
19
21
|
@cancel-create="cancelCreate()"
|
|
20
22
|
@confirm-delete="confirmDelete($event)"
|
|
23
|
+
@image-thumbnail-open="onImageThumbnailOpen"
|
|
21
24
|
/>
|
|
22
25
|
<div
|
|
23
26
|
id="organsDisplayArea"
|
|
@@ -284,29 +287,66 @@
|
|
|
284
287
|
ref="backgroundPopover"
|
|
285
288
|
:virtual-ref="backgroundIconRef"
|
|
286
289
|
placement="top-start"
|
|
287
|
-
width="
|
|
290
|
+
width="330"
|
|
288
291
|
:teleported="false"
|
|
289
292
|
trigger="click"
|
|
290
|
-
popper-class="background-popper non-selectable"
|
|
293
|
+
popper-class="background-popper non-selectable h-auto"
|
|
291
294
|
virtual-triggering
|
|
292
295
|
>
|
|
293
296
|
<div>
|
|
294
297
|
<el-row class="backgroundText">Viewing Mode</el-row>
|
|
295
|
-
<el-row class="
|
|
296
|
-
<
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
298
|
+
<el-row class="backgroundChooser">
|
|
299
|
+
<div style="margin-bottom: 2px;">
|
|
300
|
+
<template
|
|
301
|
+
v-for="(value, key, index) in viewingModes"
|
|
302
|
+
:key="key"
|
|
303
|
+
>
|
|
304
|
+
<template v-if="key === viewingMode">
|
|
305
|
+
<span class="viewing-mode-title"><b >{{ key }}</b></span>
|
|
306
|
+
</template>
|
|
307
|
+
<template v-else>
|
|
308
|
+
<span class="viewing-mode-unselected" @click="changeViewingMode(key)">{{ key }}</span>
|
|
309
|
+
</template>
|
|
310
|
+
</template>
|
|
311
|
+
</div>
|
|
312
|
+
<el-row class="viewing-mode-description">
|
|
313
|
+
{{ viewingModes[viewingMode] }}
|
|
314
|
+
</el-row>
|
|
315
|
+
</el-row>
|
|
316
|
+
<el-row class="backgroundSpacer" v-if="viewingMode === 'Exploration'"></el-row>
|
|
317
|
+
<el-row class="backgroundText" v-if="viewingMode === 'Exploration'">Markers display</el-row>
|
|
318
|
+
<el-row class="backgroundChooser" v-if="viewingMode === 'Exploration'">
|
|
319
|
+
<el-col :span="14">
|
|
320
|
+
<el-radio-group
|
|
321
|
+
v-model="imageRadio"
|
|
322
|
+
class="flatmap-radio"
|
|
323
|
+
@change="setImage"
|
|
324
|
+
>
|
|
325
|
+
<el-radio :value="false">Standard</el-radio>
|
|
326
|
+
<el-radio :value="true">Image</el-radio>
|
|
327
|
+
</el-radio-group>
|
|
328
|
+
</el-col>
|
|
329
|
+
<el-col :span="10" v-if="imageRadio">
|
|
330
|
+
<el-select
|
|
331
|
+
:teleported="false"
|
|
332
|
+
v-model="imageType"
|
|
333
|
+
placeholder="Select"
|
|
334
|
+
class="scaffold-select-box imageSelector"
|
|
335
|
+
popper-class="scaffold_viewer_dropdown"
|
|
336
|
+
@change="setImageType"
|
|
337
|
+
>
|
|
338
|
+
<el-option
|
|
339
|
+
v-for="item in imageTypes"
|
|
340
|
+
:key="item"
|
|
341
|
+
:label="item"
|
|
342
|
+
:value="item"
|
|
343
|
+
>
|
|
344
|
+
<el-row>
|
|
345
|
+
<el-col :span="12">{{ item }}</el-col>
|
|
346
|
+
</el-row>
|
|
347
|
+
</el-option>
|
|
348
|
+
</el-select>
|
|
349
|
+
</el-col>
|
|
310
350
|
</el-row>
|
|
311
351
|
<el-row class="backgroundSpacer"></el-row>
|
|
312
352
|
<el-row class="backgroundText"> Change background </el-row>
|
|
@@ -380,7 +420,7 @@
|
|
|
380
420
|
|
|
381
421
|
<script>
|
|
382
422
|
/* eslint-disable no-alert, no-console */
|
|
383
|
-
import { markRaw, shallowRef } from 'vue';
|
|
423
|
+
import { markRaw, toRaw, shallowRef } from 'vue';
|
|
384
424
|
import {
|
|
385
425
|
WarningFilled as ElIconWarningFilled,
|
|
386
426
|
ArrowDown as ElIconArrowDown,
|
|
@@ -419,8 +459,16 @@ import { AnnotationService } from '@abi-software/sparc-annotation';
|
|
|
419
459
|
import { EventNotifier } from "../scripts/EventNotifier.js";
|
|
420
460
|
import { OrgansViewer } from "../scripts/OrgansRenderer.js";
|
|
421
461
|
import { SearchIndex } from "../scripts/Search.js";
|
|
422
|
-
import { mapState } from 'pinia';
|
|
423
|
-
import { useMainStore } from "@/
|
|
462
|
+
import { mapState, mapStores } from 'pinia';
|
|
463
|
+
import { useMainStore } from "@/stores/index";
|
|
464
|
+
import { useSettingsStore } from '@/stores/settings'
|
|
465
|
+
import {
|
|
466
|
+
getBiolucidaThumbnails,
|
|
467
|
+
getSegmentationThumbnails,
|
|
468
|
+
getScaffoldThumbnails,
|
|
469
|
+
getPlotThumbnails
|
|
470
|
+
} from '../services/scicrunchQueries'
|
|
471
|
+
import imageMixin from '../mixins/imageMixin.js'
|
|
424
472
|
|
|
425
473
|
/**
|
|
426
474
|
* A vue component of the scaffold viewer.
|
|
@@ -430,6 +478,7 @@ import { useMainStore } from "@/store/index";
|
|
|
430
478
|
*/
|
|
431
479
|
export default {
|
|
432
480
|
name: "ScaffoldVuer",
|
|
481
|
+
mixins: [imageMixin],
|
|
433
482
|
components: {
|
|
434
483
|
Button,
|
|
435
484
|
Col,
|
|
@@ -564,6 +613,20 @@ export default {
|
|
|
564
613
|
type: Boolean,
|
|
565
614
|
default: false,
|
|
566
615
|
},
|
|
616
|
+
/**
|
|
617
|
+
* GroupName to value pair.
|
|
618
|
+
* The value can be a single number or and object in the following
|
|
619
|
+
* form:
|
|
620
|
+
*
|
|
621
|
+
* {
|
|
622
|
+
* number: Number,
|
|
623
|
+
* imgURL: String
|
|
624
|
+
* }
|
|
625
|
+
*
|
|
626
|
+
* When imgURL is specified, scaffoldvuer will attempt to render
|
|
627
|
+
* the image in imgURL as marker instead.
|
|
628
|
+
*
|
|
629
|
+
*/
|
|
567
630
|
markerLabels : {
|
|
568
631
|
type: Object,
|
|
569
632
|
default: function () {
|
|
@@ -673,10 +736,24 @@ export default {
|
|
|
673
736
|
/**
|
|
674
737
|
* Enable local annotations
|
|
675
738
|
*/
|
|
676
|
-
|
|
739
|
+
enableLocalAnnotations: {
|
|
677
740
|
type: Boolean,
|
|
678
741
|
default: false
|
|
679
742
|
},
|
|
743
|
+
/**
|
|
744
|
+
* Specify the endpoint of the SPARC API.
|
|
745
|
+
*/
|
|
746
|
+
sparcAPI: {
|
|
747
|
+
type: String,
|
|
748
|
+
default: 'https://api.sparc.science/',
|
|
749
|
+
},
|
|
750
|
+
/**
|
|
751
|
+
* The option to show image thumbnail in sidebar
|
|
752
|
+
*/
|
|
753
|
+
imageThumbnailSidebar: {
|
|
754
|
+
type: Boolean,
|
|
755
|
+
default: false,
|
|
756
|
+
},
|
|
680
757
|
},
|
|
681
758
|
provide() {
|
|
682
759
|
return {
|
|
@@ -684,6 +761,7 @@ export default {
|
|
|
684
761
|
scaffoldUrl: this.url,
|
|
685
762
|
$annotator: this.annotator,
|
|
686
763
|
boundingDims: this.boundingDims,
|
|
764
|
+
getFeaturesAlert: () => undefined,
|
|
687
765
|
};
|
|
688
766
|
},
|
|
689
767
|
data: function () {
|
|
@@ -774,12 +852,13 @@ export default {
|
|
|
774
852
|
active: false,
|
|
775
853
|
},
|
|
776
854
|
fileFormat: "metadata",
|
|
777
|
-
|
|
855
|
+
markerLabelEntry: markRaw({}),
|
|
856
|
+
previousMarkerLabelEntry: markRaw({}),
|
|
778
857
|
viewingMode: "Exploration",
|
|
779
|
-
viewingModes:
|
|
780
|
-
"
|
|
781
|
-
"
|
|
782
|
-
|
|
858
|
+
viewingModes: {
|
|
859
|
+
"Exploration": "View and explore detailed visualization of 3D scaffolds",
|
|
860
|
+
"Annotation": "View internal identifiers of features",
|
|
861
|
+
},
|
|
783
862
|
openMapRef: undefined,
|
|
784
863
|
backgroundIconRef: undefined,
|
|
785
864
|
userInformation: undefined,
|
|
@@ -796,6 +875,10 @@ export default {
|
|
|
796
875
|
centre: [0, 0, 0],
|
|
797
876
|
size:[1, 1, 1],
|
|
798
877
|
},
|
|
878
|
+
imageRadio: false,
|
|
879
|
+
imageType: 'Image',
|
|
880
|
+
imageTypes: ['Image', 'Segmentation', 'Scaffold', 'Plot'],
|
|
881
|
+
imageClicked: '',
|
|
799
882
|
};
|
|
800
883
|
},
|
|
801
884
|
watch: {
|
|
@@ -875,14 +958,17 @@ export default {
|
|
|
875
958
|
},
|
|
876
959
|
immediate: true,
|
|
877
960
|
},
|
|
878
|
-
markerLabels: function(labels) {
|
|
879
|
-
|
|
961
|
+
markerLabels: function (labels) {
|
|
962
|
+
this.markerLabelEntry = markRaw({...labels})
|
|
963
|
+
},
|
|
964
|
+
markerLabelEntry: function (entry) {
|
|
965
|
+
for (const [key, value] of Object.entries(this.previousMarkerLabelEntry)) {
|
|
880
966
|
this.setMarkerModeForObjectsWithName(key, value, "off");
|
|
881
967
|
}
|
|
882
|
-
for (const [key, value] of Object.entries(
|
|
968
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
883
969
|
this.setMarkerModeForObjectsWithName(key, value, "on");
|
|
884
970
|
}
|
|
885
|
-
this.
|
|
971
|
+
this.previousMarkerLabelEntry = markRaw({...entry});
|
|
886
972
|
},
|
|
887
973
|
},
|
|
888
974
|
beforeCreate: function () {
|
|
@@ -922,9 +1008,13 @@ export default {
|
|
|
922
1008
|
},
|
|
923
1009
|
computed: {
|
|
924
1010
|
...mapState(useMainStore, ['userToken']),
|
|
1011
|
+
...mapStores(useSettingsStore),
|
|
925
1012
|
annotationDisplay: function() {
|
|
926
1013
|
return this.viewingMode === 'Annotation' && this.tData.active === true &&
|
|
927
1014
|
(this.activeDrawMode === 'Edit' || this.activeDrawMode === 'Delete');
|
|
1015
|
+
},
|
|
1016
|
+
imageThumbnailsEntry: function() {
|
|
1017
|
+
return this.imageClicked ? this.convertUberonToName() : {};
|
|
928
1018
|
}
|
|
929
1019
|
},
|
|
930
1020
|
methods: {
|
|
@@ -1487,6 +1577,13 @@ export default {
|
|
|
1487
1577
|
this.$refs.scaffoldTreeControls.removeActive(false);
|
|
1488
1578
|
}
|
|
1489
1579
|
}
|
|
1580
|
+
if (this.imageRadio && event.identifiers.length && event.identifiers[0]) {
|
|
1581
|
+
this.imageClicked = event.identifiers[0].data.id
|
|
1582
|
+
? event.identifiers[0].data.id
|
|
1583
|
+
: event.identifiers[0].data.group;
|
|
1584
|
+
} else {
|
|
1585
|
+
this.imageClicked = ''
|
|
1586
|
+
}
|
|
1490
1587
|
//Emit when an object is selected
|
|
1491
1588
|
//@arg Identifier of selected objects
|
|
1492
1589
|
this.$emit("scaffold-selected", event.identifiers);
|
|
@@ -1537,6 +1634,12 @@ export default {
|
|
|
1537
1634
|
this.createEditTemporaryLines(event.identifiers[0].extraData.worldCoords);
|
|
1538
1635
|
}
|
|
1539
1636
|
this.createEditTemporaryLines(event.identifiers[0].extraData.worldCoords);
|
|
1637
|
+
const id = event.identifiers[0].data.id
|
|
1638
|
+
? event.identifiers[0].data.id
|
|
1639
|
+
: event.identifiers[0].data.group;
|
|
1640
|
+
if (this.imageClicked !== id) {
|
|
1641
|
+
this.imageClicked = ''
|
|
1642
|
+
}
|
|
1540
1643
|
}
|
|
1541
1644
|
}
|
|
1542
1645
|
}
|
|
@@ -1863,9 +1966,13 @@ export default {
|
|
|
1863
1966
|
},
|
|
1864
1967
|
/**
|
|
1865
1968
|
* Callback on viewing mode change
|
|
1969
|
+
* Optional, can be used to update the view mode.
|
|
1866
1970
|
*/
|
|
1867
|
-
|
|
1971
|
+
changeViewingMode: function (modeName) {
|
|
1868
1972
|
if (this.$module) {
|
|
1973
|
+
if (modeName) {
|
|
1974
|
+
this.viewingMode = modeName
|
|
1975
|
+
}
|
|
1869
1976
|
if (this.viewingMode === "Annotation") {
|
|
1870
1977
|
let authenticated = false;
|
|
1871
1978
|
if (this.userInformation) {
|
|
@@ -1919,13 +2026,19 @@ export default {
|
|
|
1919
2026
|
/**
|
|
1920
2027
|
* Set the marker modes for objects with the provided name, mode can
|
|
1921
2028
|
* be "on", "off" or "inherited".
|
|
2029
|
+
* Value can either be number or an object containing number and
|
|
2030
|
+
* imgURL.
|
|
1922
2031
|
*/
|
|
1923
|
-
setMarkerModeForObjectsWithName: function (name,
|
|
2032
|
+
setMarkerModeForObjectsWithName: function (name, value, mode) {
|
|
1924
2033
|
if (name && this.$module.scene) {
|
|
2034
|
+
let options = value;
|
|
2035
|
+
if (typeof value === 'number') {
|
|
2036
|
+
options = { number: value, imgURL: undefined };
|
|
2037
|
+
}
|
|
1925
2038
|
const rootRegion = this.$module.scene.getRootRegion();
|
|
1926
2039
|
const groups = [name];
|
|
1927
2040
|
const objects = findObjectsWithNames(rootRegion, groups, "", true);
|
|
1928
|
-
objects.forEach(object => object.setMarkerMode(mode,
|
|
2041
|
+
objects.forEach(object => object.setMarkerMode(mode, options));
|
|
1929
2042
|
}
|
|
1930
2043
|
},
|
|
1931
2044
|
/**
|
|
@@ -2301,15 +2414,67 @@ export default {
|
|
|
2301
2414
|
this.$module.toggleSyncControl(flag, rotateMode);
|
|
2302
2415
|
this.$module.setSyncControlCallback(this.syncControlCallback);
|
|
2303
2416
|
},
|
|
2304
|
-
|
|
2305
2417
|
/**
|
|
2306
2418
|
* Set the markers for the scene.
|
|
2307
2419
|
*/
|
|
2308
2420
|
setMarkers: function () {
|
|
2309
|
-
for (const [key, value] of Object.entries(this.
|
|
2421
|
+
for (const [key, value] of Object.entries(this.markerLabelEntry)) {
|
|
2310
2422
|
this.setMarkerModeForObjectsWithName(key, value, "on");
|
|
2311
2423
|
}
|
|
2312
2424
|
},
|
|
2425
|
+
removeImageThumbnails: function () {
|
|
2426
|
+
this.imageThumbnails = {}
|
|
2427
|
+
this.markerLabelEntry = markRaw(this.markerLabels)
|
|
2428
|
+
},
|
|
2429
|
+
setImage: function (flag) {
|
|
2430
|
+
if (flag) {
|
|
2431
|
+
this.setImageType(this.imageType)
|
|
2432
|
+
} else {
|
|
2433
|
+
this.removeImageThumbnails()
|
|
2434
|
+
}
|
|
2435
|
+
},
|
|
2436
|
+
setImageType: async function (type) {
|
|
2437
|
+
this.imageType = type
|
|
2438
|
+
if (!this.settingsStore.imageTypeCached(type)) {
|
|
2439
|
+
this.loading = true
|
|
2440
|
+
await this.fetchImageThumbnails(type)
|
|
2441
|
+
this.loading = false
|
|
2442
|
+
}
|
|
2443
|
+
this.populateImageThumbnails(type)
|
|
2444
|
+
},
|
|
2445
|
+
fetchImageThumbnails: async function (type) {
|
|
2446
|
+
let thumbnails = {}
|
|
2447
|
+
const organCuries = this.settingsStore.organCuries
|
|
2448
|
+
if (type === 'Image') {
|
|
2449
|
+
thumbnails = await getBiolucidaThumbnails(this.sparcAPI, organCuries, type)
|
|
2450
|
+
} else if (type === 'Segmentation') {
|
|
2451
|
+
thumbnails = await getSegmentationThumbnails(this.sparcAPI, organCuries, type)
|
|
2452
|
+
} else if (type === 'Scaffold') {
|
|
2453
|
+
thumbnails = await getScaffoldThumbnails(this.sparcAPI, organCuries, type)
|
|
2454
|
+
} else if (type === 'Plot') {
|
|
2455
|
+
thumbnails = await getPlotThumbnails(this.sparcAPI, organCuries, type)
|
|
2456
|
+
}
|
|
2457
|
+
this.settingsStore.updateImageThumbnails(type, thumbnails)
|
|
2458
|
+
},
|
|
2459
|
+
convertUberonToName: function () {
|
|
2460
|
+
const organCuries = this.settingsStore.organCuries
|
|
2461
|
+
const identifiers = organCuries.filter((curie) => curie.name in this.markerLabels).map((curie) => curie.id)
|
|
2462
|
+
const imageThumbnails = this.settingsStore.getImageThumbnails(this.imageType, identifiers)
|
|
2463
|
+
return Object.assign({},
|
|
2464
|
+
Object.fromEntries(
|
|
2465
|
+
Object.entries(imageThumbnails)
|
|
2466
|
+
.map(([key, value]) => [organCuries.filter((curie) => curie.id === key)[0].name, value])))
|
|
2467
|
+
},
|
|
2468
|
+
populateImageThumbnails: async function (type) {
|
|
2469
|
+
this.removeImageThumbnails()
|
|
2470
|
+
const thumbnails = this.convertUberonToName()
|
|
2471
|
+
this.loading = true
|
|
2472
|
+
this.markerLabelEntry = markRaw(await this.populateMapWithImages(thumbnails, type))
|
|
2473
|
+
this.loading = false
|
|
2474
|
+
},
|
|
2475
|
+
onImageThumbnailOpen: function (payload) {
|
|
2476
|
+
this.$emit('image-thumbnail-open', payload);
|
|
2477
|
+
},
|
|
2313
2478
|
},
|
|
2314
2479
|
};
|
|
2315
2480
|
</script>
|
|
@@ -2469,7 +2634,7 @@ export default {
|
|
|
2469
2634
|
background-color: #ffffff;
|
|
2470
2635
|
border: 1px solid $app-primary-color;
|
|
2471
2636
|
box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
|
|
2472
|
-
height:
|
|
2637
|
+
height: fit-content;
|
|
2473
2638
|
min-width: 200px;
|
|
2474
2639
|
.el-popper__arrow {
|
|
2475
2640
|
&:before {
|
|
@@ -2478,6 +2643,10 @@ export default {
|
|
|
2478
2643
|
}
|
|
2479
2644
|
}
|
|
2480
2645
|
|
|
2646
|
+
:deep(.background-popper.el-popover.el-popper.h-auto) {
|
|
2647
|
+
height: auto !important;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2481
2650
|
:deep(.open-map-popper.el-popover.el-popper) {
|
|
2482
2651
|
padding-top: 5px;
|
|
2483
2652
|
padding-bottom: 5px;
|
|
@@ -2680,15 +2849,38 @@ export default {
|
|
|
2680
2849
|
}
|
|
2681
2850
|
}
|
|
2682
2851
|
|
|
2852
|
+
.viewing-mode-title {
|
|
2853
|
+
font-size: 14px;
|
|
2854
|
+
font-weight: 600;
|
|
2855
|
+
color: $app-primary-color;
|
|
2856
|
+
margin: 8px;
|
|
2857
|
+
text-decoration: underline;
|
|
2858
|
+
cursor: pointer;
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
.viewing-mode-unselected {
|
|
2862
|
+
font-size: 11px;
|
|
2863
|
+
font-weight: 600;
|
|
2864
|
+
color: rgb(48, 49, 51);
|
|
2865
|
+
margin: 8px;
|
|
2866
|
+
opacity: 0.5;
|
|
2867
|
+
cursor: pointer;
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
.viewing-mode-description {
|
|
2871
|
+
font-size: 12px;
|
|
2872
|
+
color: rgb(48, 49, 51);
|
|
2873
|
+
text-align: left;
|
|
2874
|
+
padding-bottom: 4px;
|
|
2875
|
+
margin-left: 8px;
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2683
2878
|
.scaffold-select-box {
|
|
2684
2879
|
border-radius: 4px;
|
|
2685
2880
|
border: 1px solid rgb(144, 147, 153);
|
|
2686
2881
|
background-color: var(--white);
|
|
2687
2882
|
font-weight: 500;
|
|
2688
2883
|
color: rgb(48, 49, 51);
|
|
2689
|
-
&.viewing-mode {
|
|
2690
|
-
width: 150px!important;
|
|
2691
|
-
}
|
|
2692
2884
|
|
|
2693
2885
|
&.speed {
|
|
2694
2886
|
margin-left: 8px;
|
|
@@ -2699,6 +2891,10 @@ export default {
|
|
|
2699
2891
|
min-height: 24px
|
|
2700
2892
|
}
|
|
2701
2893
|
}
|
|
2894
|
+
|
|
2895
|
+
&.imageSelector {
|
|
2896
|
+
width: 125px!important;
|
|
2897
|
+
}
|
|
2702
2898
|
}
|
|
2703
2899
|
|
|
2704
2900
|
:deep(.scaffold_viewer_dropdown) {
|
|
@@ -79,6 +79,7 @@ import {
|
|
|
79
79
|
ElSlider as Slider,
|
|
80
80
|
ElOption as Option,
|
|
81
81
|
} from "element-plus";
|
|
82
|
+
import { markRaw } from 'vue';
|
|
82
83
|
|
|
83
84
|
/**
|
|
84
85
|
* A component to control the opacity of the target object.
|
|
@@ -116,39 +117,37 @@ export default {
|
|
|
116
117
|
label: "z",
|
|
117
118
|
},
|
|
118
119
|
],
|
|
120
|
+
zincObject: undefined,
|
|
119
121
|
};
|
|
120
122
|
},
|
|
121
|
-
mounted: function () {
|
|
122
|
-
this._zincObject = undefined;
|
|
123
|
-
},
|
|
124
123
|
methods: {
|
|
125
124
|
setObject: function (object) {
|
|
126
125
|
if (object.isTextureSlides) {
|
|
127
|
-
this.
|
|
128
|
-
this.settings = this.
|
|
126
|
+
this.zincObject = markRaw(object);
|
|
127
|
+
this.settings = this.zincObject.getTextureSettings();
|
|
129
128
|
} else {
|
|
130
|
-
this.
|
|
129
|
+
this.zincObject = undefined;
|
|
131
130
|
this.settings = [];
|
|
132
131
|
}
|
|
133
132
|
},
|
|
134
133
|
addNewSlide: function () {
|
|
135
134
|
const newSettings = { direction: "x", value: 0 };
|
|
136
|
-
const returnSettings = this.
|
|
135
|
+
const returnSettings = this.zincObject.createSlide(newSettings);
|
|
137
136
|
this.settings.push(returnSettings);
|
|
138
137
|
},
|
|
139
138
|
modifyDirection: function(direction, slide) {
|
|
140
139
|
if (slide) {
|
|
141
140
|
slide.direction = direction;
|
|
142
|
-
this.
|
|
141
|
+
this.zincObject.modifySlideSettings(slide);
|
|
143
142
|
}
|
|
144
143
|
},
|
|
145
144
|
modifySlide: function (slide) {
|
|
146
145
|
if (slide) {
|
|
147
|
-
this.
|
|
146
|
+
this.zincObject.modifySlideSettings(slide);
|
|
148
147
|
}
|
|
149
148
|
},
|
|
150
149
|
removeSlide: function (index, slide) {
|
|
151
|
-
this.
|
|
150
|
+
this.zincObject.removeSlideWithId(slide.id);
|
|
152
151
|
this.settings.splice(index, 1);
|
|
153
152
|
},
|
|
154
153
|
},
|
|
@@ -119,6 +119,7 @@ import {
|
|
|
119
119
|
ElMain as Main,
|
|
120
120
|
ElSlider as Slider,
|
|
121
121
|
} from "element-plus";
|
|
122
|
+
import { markRaw } from "vue";
|
|
122
123
|
|
|
123
124
|
/**
|
|
124
125
|
* A component to control the opacity of the target object.
|
|
@@ -141,6 +142,7 @@ export default {
|
|
|
141
142
|
scale: 1,
|
|
142
143
|
min: [0, 0, 0],
|
|
143
144
|
max: [1, 1, 1],
|
|
145
|
+
zincObject: undefined,
|
|
144
146
|
};
|
|
145
147
|
},
|
|
146
148
|
watch: {
|
|
@@ -163,14 +165,11 @@ export default {
|
|
|
163
165
|
deep: true,
|
|
164
166
|
},
|
|
165
167
|
},
|
|
166
|
-
mounted: function () {
|
|
167
|
-
this._zincObject = undefined;
|
|
168
|
-
},
|
|
169
168
|
methods: {
|
|
170
169
|
setObject: function (object) {
|
|
171
170
|
if (object.isZincObject) {
|
|
172
|
-
this.
|
|
173
|
-
const morph = this.
|
|
171
|
+
this.zincObject = markRaw(object);
|
|
172
|
+
const morph = this.zincObject.getGroup();
|
|
174
173
|
if (morph && morph.position) {
|
|
175
174
|
this.x = morph.position.x;
|
|
176
175
|
this.y = morph.position.y;
|
|
@@ -178,7 +177,7 @@ export default {
|
|
|
178
177
|
this.scale = morph.scale.x;
|
|
179
178
|
}
|
|
180
179
|
} else {
|
|
181
|
-
this.
|
|
180
|
+
this.zincObject = undefined;
|
|
182
181
|
this.x = 0;
|
|
183
182
|
this.y = 0;
|
|
184
183
|
this.z = 0;
|
|
@@ -186,10 +185,10 @@ export default {
|
|
|
186
185
|
}
|
|
187
186
|
},
|
|
188
187
|
modifyPosition: function() {
|
|
189
|
-
this.
|
|
188
|
+
this.zincObject.setPosition(this.x, this.y, this.z);
|
|
190
189
|
},
|
|
191
190
|
modifyScale: function() {
|
|
192
|
-
this.
|
|
191
|
+
this.zincObject.setScaleAll(this.scale);
|
|
193
192
|
},
|
|
194
193
|
},
|
|
195
194
|
};
|
package/src/components.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ declare module 'vue' {
|
|
|
26
26
|
ElMain: typeof import('element-plus/es')['ElMain']
|
|
27
27
|
ElOption: typeof import('element-plus/es')['ElOption']
|
|
28
28
|
ElPopover: typeof import('element-plus/es')['ElPopover']
|
|
29
|
+
ElRadio: typeof import('element-plus/es')['ElRadio']
|
|
30
|
+
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
|
29
31
|
ElRow: typeof import('element-plus/es')['ElRow']
|
|
30
32
|
ElSelect: typeof import('element-plus/es')['ElSelect']
|
|
31
33
|
ElSlider: typeof import('element-plus/es')['ElSlider']
|
package/src/main.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createApp } from 'vue'
|
|
|
2
2
|
import { createPinia } from 'pinia'
|
|
3
3
|
import * as VueRouter from 'vue-router'
|
|
4
4
|
import App from './App.vue'
|
|
5
|
-
import { useMainStore } from '@/
|
|
5
|
+
import { useMainStore } from '@/stores/index'
|
|
6
6
|
|
|
7
7
|
const routes = [
|
|
8
8
|
{ path: '/'},
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
methods: {
|
|
3
|
+
populateMapWithImages: async function (images, type) {
|
|
4
|
+
let imageMarkerLabels = {};
|
|
5
|
+
for (const [key, list] of Object.entries(images)) {
|
|
6
|
+
const response = await this.downloadImageThumbnail(key, list, type);
|
|
7
|
+
if (response) {
|
|
8
|
+
imageMarkerLabels[key] = response;
|
|
9
|
+
} else {
|
|
10
|
+
imageMarkerLabels[key] = this.markerLabels[key];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return imageMarkerLabels;
|
|
14
|
+
},
|
|
15
|
+
downloadImageThumbnail: async function (key, list, type) {
|
|
16
|
+
const count = list.length;
|
|
17
|
+
if (count > 0) {
|
|
18
|
+
//Pick a random image
|
|
19
|
+
const index = Math.floor(Math.random() * count);
|
|
20
|
+
const thumbnail = list[index].thumbnail;
|
|
21
|
+
try {
|
|
22
|
+
const response = await this.getImageThumbnail(thumbnail, type);
|
|
23
|
+
const markerObject = await this.addImageThumbnailMarker(key, response);
|
|
24
|
+
return markerObject;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
// Failed to download, pick another one
|
|
27
|
+
list.splice(index);
|
|
28
|
+
this.downloadImageThumbnail(key, list, type);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
getImageThumbnail: async function (url, type) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
if (type === "Image" || type === "Segmentation") {
|
|
35
|
+
this.getBinaryThumbnail(url)
|
|
36
|
+
.then((response) => resolve(response))
|
|
37
|
+
.catch((response) => reject(response));
|
|
38
|
+
} else {
|
|
39
|
+
this.getGenericThumbnail(url)
|
|
40
|
+
.then((response) => resolve(response))
|
|
41
|
+
.catch((response) => reject(response));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
getBinaryThumbnail: async function (url) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
fetch(url)
|
|
48
|
+
.then((response) => {
|
|
49
|
+
if (response.status >= 200 && response.status < 300) {
|
|
50
|
+
return response.text();
|
|
51
|
+
} else {
|
|
52
|
+
reject();
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
.then((data) => {
|
|
56
|
+
if (data) {
|
|
57
|
+
let img = new Image();
|
|
58
|
+
img.onload = function () {
|
|
59
|
+
resolve(`data:'image/png';base64,${data}`);
|
|
60
|
+
};
|
|
61
|
+
img.onerror = function () {
|
|
62
|
+
reject(new Error("Failed to load image at " + url));
|
|
63
|
+
};
|
|
64
|
+
img.src = `data:'image/png';base64,${data}`;
|
|
65
|
+
} else {
|
|
66
|
+
reject(new Error("Failed to load image at " + url));
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
getGenericThumbnail: async function (url) {
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
let img = new Image();
|
|
74
|
+
img.onload = function () {
|
|
75
|
+
resolve(url);
|
|
76
|
+
};
|
|
77
|
+
img.onerror = function () {
|
|
78
|
+
reject(new Error("Failed to load image at " + url));
|
|
79
|
+
};
|
|
80
|
+
img.src = url;
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
addImageThumbnailMarker: async function (id, source) {
|
|
84
|
+
const blob = await (await fetch(source)).blob();
|
|
85
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
86
|
+
return { number: this.markerLabels[id], imgURL: blobUrl };
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|