Hey,
Thanks for looking into it!
- I am using MacOS with Chrome and Arc, but I have seen it crashing on Windows in Chrome.
- It’s just loading a single model, I attached the component code.
- The app is not publicly accessible and it is hosted as a Docker image in Google Cloud Run. The node has sufficient memory and cpu allocated.
- It does not crash always, I would say 70% of time.
<template>
<v-row>
<v-col cols="12">
<div class="speckle-viewer" ref="speckle-viewer" :id="viewerId">
<loading v-if="loading"></loading>
<template v-else>
<v-btn
class="clear-button"
small
color="primary"
v-if="selectedSpeckle"
>CLEAR SELECTION</v-btn
>
<template v-if="showUpdate">
<v-btn
icon
color="primary"
class="update-button"
@click.native="$emit('update-model')">
<v-icon>mdi-tray-arrow-up</v-icon>
</v-btn>
</template>
<template v-if="showMeasure">
<v-btn
icon
color="primary"
class="measure-button"
@click.native="measurements.enabled = true"
v-if="!measurements.enabled">
<v-icon>mdi-ruler</v-icon>
</v-btn>
<template v-if="measurements.enabled">
<v-btn
icon
color="primary"
class="measure-button"
@click.native="toggleMeasurements">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-btn
icon
color="primary"
class="clear-measurements-button"
@click.native="clearMeasurements">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
</template>
</template>
</div>
</v-col>
</v-row>
</template>
<script>
import {
CameraController,
DefaultViewerParams,
FilteringExtension,
MeasurementsExtension,
SelectionExtension,
SpeckleLoader,
Viewer,
ViewerEvent,
} from "@speckle/viewer";
import Loading from "~/components/common/Loading.vue";
export default {
components: { Loading },
props: {
viewerId: {
type: String,
required: true,
},
speckleUrl: {
type: String,
default: null,
},
objectIds: {
type: Array,
default: () => [],
},
isolate: {
type: Boolean,
default: false,
},
hide: {
type: Boolean,
default: false,
},
setSpeckle: {
type: Boolean,
default: false,
},
showMeasure: {
type: Boolean,
default: false,
},
showUpdate: {
type: Boolean,
default: false,
},
},
watch: {
selectedSpeckle: {
handler(value) {
this.selector.clearSelection();
// this.camera.zoom();
if (value) {
if (this.viewer) {
this.selector.selectObjects([value]);
// this.camera.zoom([value], true, 100);
}
}
},
},
hide: {
handler(value) {
if (value) {
this.filter.hideObjects(this.objectIds);
}
},
},
speckleUrl: {
async handler() {
if (this.viewer) {
await this.viewer.unloadAll();
await this.loadModel();
}
},
},
},
data() {
return {
loading: true,
viewer: null,
camera: null,
filter: null,
selector: null,
measurements: null,
};
},
computed: {
selectedSpeckle: {
get() {
return this.$store.getters["twins/selectedSpeckle"];
},
set(value) {
this.$store.commit("twins/setSelectedSpeckle", value);
},
},
},
methods: {
async initViewer() {
try {
this.loading = true;
/** Get the HTML container */
const container = document.getElementById(this.viewerId);
/** Configure the viewer params */
const params = DefaultViewerParams;
// params.showStats = true;
// params.verbose = true;
/** Create Viewer instance and initalize */
const viewer = new Viewer(container, params);
await viewer.init();
this.camera = viewer.createExtension(CameraController);
this.selector = viewer.createExtension(SelectionExtension);
this.filter = viewer.createExtension(FilteringExtension);
if (this.showMeasure)
this.measurements = viewer.createExtension(MeasurementsExtension);
viewer.on(ViewerEvent.ObjectClicked, this.handleObjectClicked);
this.viewer = viewer;
await this.loadModel();
this.loading = false;
} catch (err) {
console.log(err);
}
},
async loadModel() {
try {
if (!this.viewer) {
this.initViewer();
}
/** Create a loader for the speckle stream */
const loader = new SpeckleLoader(
this.viewer.getWorldTree(),
this.speckleUrl,
process.env.SPECKLE_KEY,
true
);
/** Load the speckle data */
await this.viewer.unloadAll(); // Unload all
await this.viewer.loadObject(loader, true);
if (this.selectedSpeckle) {
this.selector.selectObjects([this.selectedSpeckle]);
}
} catch (err) {
console.log(err);
}
},
handleObjectClicked(selectionInfo) {
if (this.measurements && this.measurements.enabled) return;
if (selectionInfo && selectionInfo.hits.length > 0) {
if (this.setSpeckle)
this.selectedSpeckle = selectionInfo.hits[0].node.model.id;
this.$emit("object-clicked", selectionInfo.hits[0].node.model);
} else {
// No object clicked. Restore focus to entire scene
if (this.setSpeckle) this.selectedSpeckle = null;
this.$emit("object-clicked", null);
}
},
toggleMeasurements() {
this.measurements.enabled = !this.measurements.enabled;
},
clearMeasurements() {
this.measurements.clearMeasurements();
},
},
async beforeDestroy() {
if (this.viewer) {
await this.viewer.unloadAll();
this.viewer.dispose();
this.viewer = null;
this.camera = null;
this.selector = null;
this.filter = null;
this.measurements = null;
}
},
async mounted() {
await this.initViewer();
if (this.objectIds.length > 0 && this.hide) {
this.filter.hideObjects(this.objectIds);
}
},
};
</script>