import { sdk } from './sdk' import { toRaw, ref, watch, nextTick } from 'vue' import { viewModeStack, showLeftPanoStack, custom, getResource } from '@/env' import { mount, diffArrayChange, shallowWatchArray, arrayChildEffectScope, showLoad, hideLoad, deepIsRevise, round, togetherCallback } from '@/utils' import { fuseModels, taggings, isEdit, sysBus, getFuseModelShowVariable, SceneType, backupFuseModels, MeasureType, measures } from '@/store' import TaggingComponent from '@/components/tagging/list.vue' import type { FuseModel, Tagging, Measure } from '@/store' import type { SDK, SceneModel, SceneGuidePath, ModelAttrRange, Measure as SceneMeasure } from '.' let isUnSet = false const unSet = ((fn: () => void) => { nextTick(() => { isUnSet = true fn() nextTick(() => isUnSet = false) }) }) // -----------------模型关联-------------------- export const modelRange: ModelAttrRange = { opacityRange: { min: 0, max: 100, step: 0.1 }, bottomRange: { min: -30, max: 70, step: 0.1 }, scaleRange: { min: 0, max: 200, step: 0.1 } } const sceneModelMap = new WeakMap() export const getSceneModel = (model?: FuseModel | null) => model && sceneModelMap.get(toRaw(model)) const associationModels = (sdk: SDK) => { const getModels = () => fuseModels.value shallowWatchArray(getModels, (models, oldModels) => { const { added, deleted } = diffArrayChange(models, oldModels) for (const item of added) { if (getSceneModel(item)) { continue; } const itemRaw = toRaw(item) const sceneModel = sdk.addModel({ ...itemRaw, ...modelRange, type: item.type === SceneType.SWSS ? 'laser' : 'glb', url: item.type === SceneType.SWSS ? item.url : getResource(item.url) }) sceneModelMap.set(itemRaw, sceneModel) sceneModel.bus.on('transformChanged', transform => { transform = { ...transform } if (transform.rotation) { transform.rotation = { x: round(transform.rotation.x, 5), y: round(transform.rotation.y, 5), z: round(transform.rotation.z, 5), } } if (transform.position) { transform.position = { x: round(transform.position.x, 5), y: round(transform.position.y, 5), z: round(transform.position.z, 5), } } const updateKeys = Object.keys(transform) const update: any = {} for (const key of updateKeys) { update[key] = (item as any)[key] } if (deepIsRevise(update, transform)) { unSet(() => Object.assign(item, transform)) } }) sceneModel.bus.on('changeSelect', select => { if (custom.currentModel === item && !select) { custom.currentModel = null } else if (custom.currentModel !== item && select) { custom.currentModel = item } }) showLoad() sceneModel.bus.on('loadDone', () => { item.loaded = true hideLoad() backupFuseModels() }) sceneModel.bus.on('loadError', () => { item.error = true item.show = false custom.showModelsMap.delete(item) hideLoad() backupFuseModels() }) sceneModel.bus.on('loadProgress', progress => item.progress = progress) } for (const item of deleted) { getSceneModel(item)?.destroy() } }) arrayChildEffectScope(getModels, item => { const stopLoadedWatch = watch( () => item.loaded, (loaded) => { if (loaded) { const modelShow = getFuseModelShowVariable(item) watch( () => item.bottom, () => isUnSet || getSceneModel(item)?.changeBottom(item.bottom), { immediate: true } ) watch( () => item.opacity, () => isUnSet || getSceneModel(item)?.changeOpacity(item.opacity), { immediate: true } ) watch( () => item.scale, () => isUnSet || getSceneModel(item)?.changeScale(item.scale), { immediate: true } ) watch( () => item.position, () => isUnSet || getSceneModel(item)?.changePosition(item.position), { immediate: true } ) watch( () => item.rotation, () => isUnSet || getSceneModel(item)?.changeRotation(item.rotation), { immediate: true } ) watch( () => modelShow.value, () => isUnSet || getSceneModel(item)?.changeShow(modelShow.value), { immediate: true } ) stopLoadedWatch() } } ) }) } // -----------------热点关联-------------------- const associationTaggings = (el: HTMLDivElement) => { const getTaggings = () => taggings.value const taggingVMs = new WeakMap>() shallowWatchArray(getTaggings, (taggings, oldTaggings) => { const { added, deleted } = diffArrayChange(taggings, oldTaggings) for (const item of added) { taggingVMs.set(toRaw(item), mount(el, TaggingComponent, { tagging: item })) } for (const item of deleted) { const unMount = taggingVMs.get(toRaw(item)) unMount && unMount() } }) } // -----------------测量关联-------------------- const sceneMeasureMap = new WeakMap() export const getSceneMeasure = (measure?: Measure | null) => measure && sceneMeasureMap.get(toRaw(measure)) export const associationMessaure = (smMeasure: SceneMeasure, measure: Measure) => { smMeasure.bus.on('update', ([points, modelIds]) => { unSet(() => { measure.positions = points.map((point, i) => ({ point, modelId: modelIds[i] })) measure.desc = measure.type === MeasureType.area ? (smMeasure as unknown as SceneMeasure).getArea().toString() : (smMeasure as unknown as SceneMeasure).getDistance().toString() }) }) smMeasure.bus.on('highlight', selected => unSet(() => measure.selected = selected)) } const associationMessaures = (sdk: SDK) => { const getMeasures = () => measures.value shallowWatchArray(getMeasures, (measures, oldMeasures) => { const { added, deleted } = diffArrayChange(measures, oldMeasures) for (const item of added) { const sceneMeasure = sdk.drawMeasure( item.type, item.positions.map(position => position.point), item.positions.map(position => position.modelId), ) associationMessaure(sceneMeasure, item) sceneMeasureMap.set(item, sceneMeasure) } for (const item of deleted) { const sceneMeasure = getSceneMeasure(item) sceneMeasure && sceneMeasure.destroy() } }) arrayChildEffectScope(getMeasures, measure => { watch( () => measure.selected, (selected = false) => isUnSet || getSceneMeasure(measure)?.changeSelect(selected) ) watch( () => custom.showMeasures, (show) => { if (!isUnSet) { const smMeasure = getSceneMeasure(measure) if (show) { smMeasure?.show() } else { smMeasure?.hide() } } }, { immediate: true } ) }) } // -----------------导览关联-------------------- const fullView = async (fn: () => void) => { const popViewMode = togetherCallback([ viewModeStack.push(ref('full')), showLeftPanoStack.push(ref(false)) ]) await document.documentElement.requestFullscreen() const driving = () => document.fullscreenElement || fn() document.addEventListener('fullscreenchange', driving) document.addEventListener('fullscreenerror', fn) return () => { popViewMode() document.fullscreenElement && document.exitFullscreen() document.removeEventListener('fullscreenchange', driving) document.removeEventListener('fullscreenerror', fn) } } export const isScenePlayIng = ref(false) export const playSceneGuide = async (paths: SceneGuidePath[], changeIndexCallback?: (index: number) => void) => { if (isScenePlayIng.value) { throw new Error('导览正在播放') } isScenePlayIng.value = true const sceneGuide = sdk.enterSceneGuide(paths) changeIndexCallback && sceneGuide.bus.on('changePoint', changeIndexCallback) const quitHandler = () => (isScenePlayIng.value = false) const clearHandler = isEdit.value ? null : await fullView(quitHandler) if (!clearHandler) { sysBus.on('leave', quitHandler, { last: true }) sysBus.on('save', quitHandler, { last: true }) } sceneGuide.play() const reces = [ new Promise(resolve => sceneGuide.bus.on('playComplete', resolve)), new Promise(resolve => { const stop = watch(isScenePlayIng, () => { if (!isScenePlayIng.value) { resolve() sceneGuide.pause() stop() } }) }), ] await Promise.race(reces) isScenePlayIng.value = false if (clearHandler) { clearHandler() } else { sysBus.off('leave', quitHandler) sysBus.off('save', quitHandler) } sceneGuide.clear() sceneGuide.bus.off('changePoint') } export const pauseSceneGuide = () => isScenePlayIng.value = false // -----------------启动关联-------------------- export const setupAssociation = (mountEl: HTMLDivElement) => { associationModels(sdk) associationTaggings(mountEl) associationMessaures(sdk) }