association.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. import { sdk } from './sdk'
  2. import { toRaw, ref, watch, nextTick } from 'vue'
  3. import {
  4. viewModeStack,
  5. showLeftPanoStack,
  6. custom,
  7. getResource
  8. } from '@/env'
  9. import {
  10. mount,
  11. diffArrayChange,
  12. shallowWatchArray,
  13. arrayChildEffectScope,
  14. showLoad,
  15. hideLoad,
  16. deepIsRevise,
  17. round,
  18. togetherCallback
  19. } from '@/utils'
  20. import {
  21. fuseModels,
  22. taggings,
  23. isEdit,
  24. sysBus,
  25. getFuseModelShowVariable,
  26. SceneType,
  27. backupFuseModels,
  28. MeasureType,
  29. measures
  30. } from '@/store'
  31. import TaggingComponent from '@/components/tagging/list.vue'
  32. import type { FuseModel, Tagging, Measure } from '@/store'
  33. import type {
  34. SDK,
  35. SceneModel,
  36. SceneGuidePath,
  37. ModelAttrRange,
  38. Measure as SceneMeasure
  39. } from '.'
  40. let isUnSet = false
  41. const unSet = ((fn: () => void) => {
  42. nextTick(() => {
  43. isUnSet = true
  44. fn()
  45. nextTick(() => isUnSet = false)
  46. })
  47. })
  48. // -----------------模型关联--------------------
  49. export const modelRange: ModelAttrRange = {
  50. opacityRange: { min: 0, max: 100, step: 0.1 },
  51. bottomRange: { min: -30, max: 70, step: 0.1 },
  52. scaleRange: { min: 0, max: 200, step: 0.1 }
  53. }
  54. const sceneModelMap = new WeakMap<FuseModel, SceneModel>()
  55. export const getSceneModel = (model?: FuseModel | null) => model && sceneModelMap.get(toRaw(model))
  56. const associationModels = (sdk: SDK) => {
  57. const getModels = () => fuseModels.value
  58. shallowWatchArray(getModels, (models, oldModels) => {
  59. const { added, deleted } = diffArrayChange(models, oldModels)
  60. for (const item of added) {
  61. if (getSceneModel(item)) {
  62. continue;
  63. }
  64. console.error('added', item)
  65. const itemRaw = toRaw(item)
  66. const sceneModel = sdk.addModel({
  67. ...itemRaw,
  68. ...modelRange,
  69. type: item.type === SceneType.SWSS ? 'laser' : 'glb',
  70. url: item.type === SceneType.SWSS ? item.url : getResource(item.url)
  71. })
  72. sceneModelMap.set(itemRaw, sceneModel)
  73. sceneModel.bus.on('transformChanged', transform => {
  74. transform = { ...transform }
  75. if (transform.rotation) {
  76. transform.rotation = {
  77. x: round(transform.rotation.x, 5),
  78. y: round(transform.rotation.y, 5),
  79. z: round(transform.rotation.z, 5),
  80. }
  81. }
  82. if (transform.position) {
  83. transform.position = {
  84. x: round(transform.position.x, 5),
  85. y: round(transform.position.y, 5),
  86. z: round(transform.position.z, 5),
  87. }
  88. }
  89. const updateKeys = Object.keys(transform)
  90. const update: any = {}
  91. for (const key of updateKeys) {
  92. update[key] = (item as any)[key]
  93. }
  94. if (deepIsRevise(update, transform)) {
  95. unSet(() => Object.assign(item, transform))
  96. }
  97. })
  98. sceneModel.bus.on('changeSelect', select => {
  99. if (custom.currentModel === item && !select) {
  100. custom.currentModel = null
  101. } else if (custom.currentModel !== item && select) {
  102. custom.currentModel = item
  103. }
  104. })
  105. showLoad()
  106. sceneModel.bus.on('loadDone', () => {
  107. item.loaded = true
  108. hideLoad()
  109. backupFuseModels()
  110. })
  111. sceneModel.bus.on('loadError', () => {
  112. item.error = true
  113. item.show = false
  114. custom.showModelsMap.delete(item)
  115. hideLoad()
  116. backupFuseModels()
  117. })
  118. sceneModel.bus.on('loadProgress', progress => item.progress = progress)
  119. }
  120. for (const item of deleted) {
  121. getSceneModel(item)?.destroy()
  122. }
  123. })
  124. arrayChildEffectScope(getModels, item => {
  125. const stopLoadedWatch = watch(
  126. () => item.loaded,
  127. (loaded) => {
  128. if (loaded) {
  129. const modelShow = getFuseModelShowVariable(item)
  130. watch(
  131. () => item.bottom,
  132. () => isUnSet || getSceneModel(item)?.changeBottom(item.bottom),
  133. { immediate: true }
  134. )
  135. watch(
  136. () => item.opacity,
  137. () => isUnSet || getSceneModel(item)?.changeOpacity(item.opacity),
  138. { immediate: true }
  139. )
  140. watch(
  141. () => item.scale,
  142. () => isUnSet || getSceneModel(item)?.changeScale(item.scale),
  143. { immediate: true }
  144. )
  145. watch(
  146. () => item.position,
  147. () => isUnSet || getSceneModel(item)?.changePosition(item.position),
  148. { immediate: true }
  149. )
  150. watch(
  151. () => item.rotation,
  152. () => isUnSet || getSceneModel(item)?.changeRotation(item.rotation),
  153. { immediate: true }
  154. )
  155. watch(
  156. () => modelShow.value,
  157. () => {
  158. console.error(item, modelShow.value)
  159. isUnSet || getSceneModel(item)?.changeShow(modelShow.value)
  160. },
  161. { immediate: true }
  162. )
  163. stopLoadedWatch()
  164. }
  165. }
  166. )
  167. })
  168. }
  169. // -----------------热点关联--------------------
  170. const associationTaggings = (el: HTMLDivElement) => {
  171. const getTaggings = () => taggings.value
  172. const taggingVMs = new WeakMap<Tagging, ReturnType<typeof mount>>()
  173. shallowWatchArray(getTaggings, (taggings, oldTaggings) => {
  174. const { added, deleted } = diffArrayChange(taggings, oldTaggings)
  175. for (const item of added) {
  176. taggingVMs.set(toRaw(item), mount(el, TaggingComponent, { tagging: item }))
  177. }
  178. for (const item of deleted) {
  179. const unMount = taggingVMs.get(toRaw(item))
  180. unMount && unMount()
  181. }
  182. })
  183. }
  184. // -----------------测量关联--------------------
  185. const sceneMeasureMap = new WeakMap<Measure , SceneMeasure>()
  186. export const getSceneMeasure = (measure?: Measure | null) => measure && sceneMeasureMap.get(toRaw(measure))
  187. export const associationMessaure = <T extends MeasureType>(smMeasure: SceneMeasure<T>, measure: Measure<T>) => {
  188. smMeasure.bus.on('update', ([points, modelIds]) => {
  189. unSet(() => {
  190. measure.positions = points.map((point, i) => ({ point, modelId: modelIds[i] }))
  191. measure.desc = measure.type === MeasureType.area
  192. ? (smMeasure as unknown as SceneMeasure<MeasureType.area>).getArea().toString()
  193. : (smMeasure as unknown as SceneMeasure<MeasureType.free>).getDistance().toString()
  194. })
  195. })
  196. smMeasure.bus.on('highlight', selected => unSet(() => measure.selected = selected))
  197. }
  198. const associationMessaures = (sdk: SDK) => {
  199. const getMeasures = () => measures.value
  200. shallowWatchArray(getMeasures, (measures, oldMeasures) => {
  201. const { added, deleted } = diffArrayChange(measures, oldMeasures)
  202. for (const item of added) {
  203. const sceneMeasure = sdk.drawMeasure(
  204. item.type,
  205. item.positions.map(position => position.point),
  206. item.positions.map(position => position.modelId),
  207. )
  208. associationMessaure(sceneMeasure, item)
  209. sceneMeasureMap.set(item, sceneMeasure)
  210. }
  211. for (const item of deleted) {
  212. const sceneMeasure = getSceneMeasure(item)
  213. sceneMeasure && sceneMeasure.destroy()
  214. }
  215. })
  216. arrayChildEffectScope(getMeasures, measure => {
  217. watch(
  218. () => measure.selected,
  219. (selected = false) => isUnSet || getSceneMeasure(measure)?.changeSelect(selected)
  220. )
  221. watch(
  222. () => custom.showMeasures,
  223. (show) => {
  224. if (!isUnSet) {
  225. const smMeasure = getSceneMeasure(measure)
  226. if (show) {
  227. smMeasure?.show()
  228. } else {
  229. smMeasure?.hide()
  230. }
  231. }
  232. },
  233. { immediate: true }
  234. )
  235. })
  236. }
  237. // -----------------导览关联--------------------
  238. const fullView = async (fn: () => void) => {
  239. const popViewMode = togetherCallback([
  240. viewModeStack.push(ref('full')),
  241. showLeftPanoStack.push(ref(false))
  242. ])
  243. await document.documentElement.requestFullscreen()
  244. const driving = () => document.fullscreenElement || fn()
  245. document.addEventListener('fullscreenchange', driving)
  246. document.addEventListener('fullscreenerror', fn)
  247. return () => {
  248. popViewMode()
  249. document.fullscreenElement && document.exitFullscreen()
  250. document.removeEventListener('fullscreenchange', driving)
  251. document.removeEventListener('fullscreenerror', fn)
  252. }
  253. }
  254. export const isScenePlayIng = ref(false)
  255. export const playSceneGuide = async (paths: SceneGuidePath[], changeIndexCallback?: (index: number) => void) => {
  256. if (isScenePlayIng.value) {
  257. throw new Error('导览正在播放')
  258. }
  259. isScenePlayIng.value = true
  260. const sceneGuide = sdk.enterSceneGuide(paths)
  261. changeIndexCallback && sceneGuide.bus.on('changePoint', changeIndexCallback)
  262. const quitHandler = () => (isScenePlayIng.value = false)
  263. const clearHandler = isEdit.value ? null : await fullView(quitHandler)
  264. if (!clearHandler) {
  265. sysBus.on('leave', quitHandler, { last: true })
  266. sysBus.on('save', quitHandler, { last: true })
  267. }
  268. sceneGuide.play()
  269. const reces = [
  270. new Promise(resolve => sceneGuide.bus.on('playComplete', resolve)),
  271. new Promise<void>(resolve => {
  272. const stop = watch(isScenePlayIng, () => {
  273. if (!isScenePlayIng.value) {
  274. resolve()
  275. sceneGuide.pause()
  276. stop()
  277. }
  278. })
  279. }),
  280. ]
  281. await Promise.race(reces)
  282. isScenePlayIng.value = false
  283. if (clearHandler) {
  284. clearHandler()
  285. } else {
  286. sysBus.off('leave', quitHandler)
  287. sysBus.off('save', quitHandler)
  288. }
  289. sceneGuide.clear()
  290. sceneGuide.bus.off('changePoint')
  291. }
  292. export const pauseSceneGuide = () => isScenePlayIng.value = false
  293. // -----------------启动关联--------------------
  294. export const setupAssociation = (mountEl: HTMLDivElement) => {
  295. associationModels(sdk)
  296. associationTaggings(mountEl)
  297. associationMessaures(sdk)
  298. }