t23-threejs-enhancement.mjs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import assert from 'node:assert/strict'
  2. import { readFileSync } from 'node:fs'
  3. import vm from 'node:vm'
  4. import * as Cesium from 'cesium'
  5. import { enhancementSelection, PIPE_ENHANCEMENT_KIND, planPipeEnhancement } from '../../../src/modules/cockpit/domain/pipeEnhancement.js'
  6. import { mountPipeEnhancement } from '../../../src/modules/cockpit/services/threePipeEnhancement.js'
  7. import { createPinia, setActivePinia } from 'pinia'
  8. import { usePipeEnhancementStore } from '../../../src/modules/cockpit/stores/pipeEnhancementStore.js'
  9. setActivePinia(createPinia())
  10. const enhancementStore = usePipeEnhancementStore()
  11. const pipe = { code: 'P-001', name: '城南燃气管段', industry: 'GAS', diameter: 160, material: '钢', positions: [[110.1, 28.4], [110.101, 28.401]] }
  12. const component = { code: 'V-001', name: '城南阀门', industry: 'GAS', kind: 'valve', position: [110.1, 28.4] }
  13. const base = { visiblePipes: [pipe], visibleComponents: [component] }
  14. let checks = 0
  15. function check(name, fn) { checks += 1; fn(); console.log(`PASS ${name}`) }
  16. check('Pinia store 持有并清理局部增强选择', () => {
  17. enhancementStore.select(PIPE_ENHANCEMENT_KIND.PIPE, 'P-001')
  18. assert.equal(enhancementStore.selected.kind, PIPE_ENHANCEMENT_KIND.PIPE)
  19. assert.equal(enhancementStore.selected.code, 'P-001')
  20. enhancementStore.clear()
  21. assert.equal(enhancementStore.selected, null)
  22. })
  23. check('没有选中对象时不创建增强计划', () => {
  24. assert.equal(planPipeEnhancement(base).visible, false)
  25. assert.equal(planPipeEnhancement(base).reason, 'no-selection')
  26. })
  27. check('局部计划只保留当前被选中的管段', () => {
  28. const plan = planPipeEnhancement({ ...base, selected: enhancementSelection(PIPE_ENHANCEMENT_KIND.PIPE, 'P-001') })
  29. assert.equal(plan.visible, true)
  30. assert.equal(plan.object.code, 'P-001')
  31. assert.equal(plan.object.path.length, 2)
  32. assert.match(plan.disclaimer, /不代表工程竣工模型/)
  33. })
  34. check('筛选移除对象时增强视图自动不可见', () => {
  35. const plan = planPipeEnhancement({ selected: enhancementSelection('pipe', 'P-001'), visiblePipes: [] })
  36. assert.equal(plan.visible, false)
  37. assert.equal(plan.reason, 'object-not-visible')
  38. })
  39. check('构件计划保留构件类型但不伪造管径', () => {
  40. const plan = planPipeEnhancement({ ...base, selected: enhancementSelection(PIPE_ENHANCEMENT_KIND.COMPONENT, 'V-001') })
  41. assert.equal(plan.visible, true)
  42. assert.equal(plan.object.renderKind, 'valve')
  43. assert.equal(plan.object.diameter, null)
  44. })
  45. check('未知选择类型被拒绝而不是默认成管段', () => {
  46. const plan = planPipeEnhancement({ ...base, selected: { kind: 'foreign', code: 'P-001' } })
  47. assert.equal(plan.visible, false)
  48. assert.equal(plan.reason, 'invalid-selection')
  49. })
  50. class Canvas {
  51. constructor() { this.listeners = new Map(); this.parentNode = null }
  52. addEventListener(name, fn) { this.listeners.set(name, fn) }
  53. removeEventListener(name) { this.listeners.delete(name) }
  54. }
  55. class Host {
  56. constructor() { this.clientWidth = 320; this.clientHeight = 220; this.children = []; this.ownerDocument = { createElement: () => new Canvas() } }
  57. appendChild(node) { node.parentNode = this; this.children.push(node) }
  58. removeChild(node) { this.children = this.children.filter((item) => item !== node); node.parentNode = null }
  59. }
  60. function rendererFactory({ canvas }) {
  61. const domElement = canvas || new Canvas()
  62. return { domElement, setPixelRatio() {}, setSize() {}, render() {}, dispose() {} }
  63. }
  64. check('渲染服务支持相同编码但不同对象类型的替换', () => {
  65. const host = new Host()
  66. const first = planPipeEnhancement({ ...base, selected: enhancementSelection('pipe', 'P-001') })
  67. const viewer = mountPipeEnhancement(host, first, { rendererFactory })
  68. const sameCodeComponent = { ...component, code: 'P-001' }
  69. const second = planPipeEnhancement({ visiblePipes: [pipe], visibleComponents: [sameCodeComponent], selected: enhancementSelection('component', 'P-001') })
  70. viewer.sync(second)
  71. assert.equal(viewer.getState().title, '地下管线详情视图 · 构件')
  72. assert.equal(viewer.getState().code, 'P-001')
  73. viewer.destroy()
  74. assert.equal(viewer.getState().status, 'destroyed')
  75. })
  76. const engineSource = readFileSync(new URL('../../../src/modules/cockpit/services/cesiumEngine.js', import.meta.url), 'utf8')
  77. const engineCode = engineSource
  78. .replace(/^import\s+[^]*?from\s+['"][^'"\n]+['"][ \t]*$/gm, '')
  79. .replace(/^import\s+['"][^'"\n]+['"][ \t]*$/gm, '')
  80. .replace(/^export /gm, '')
  81. const engineContext = vm.createContext({ ...Cesium, CesiumMath: Cesium.Math })
  82. vm.runInContext(`${engineCode}\nglobalThis.pipePicker = pickPipeObjectAt;`, engineContext)
  83. const pipeSource = new Cesium.CustomDataSource('cockpit-pipelines')
  84. const entity = pipeSource.entities.add({ id: 'pipe:P-001', properties: { kind: 'pipe', code: 'P-001' } })
  85. let picked = { id: entity }
  86. const pickViewer = {
  87. dataSources: { getByName: (name) => name === 'cockpit-pipelines' ? [pipeSource] : [] },
  88. canvas: { getBoundingClientRect: () => ({ left: 20, top: 30 }) },
  89. scene: { pick: () => picked },
  90. }
  91. check('Cesium entity pick returns the selected pipeline identity', () => {
  92. const hit = engineContext.pipePicker(pickViewer, 120, 230)
  93. assert.equal(hit.kind, 'pipeline')
  94. assert.equal(hit.code, 'P-001')
  95. assert.equal(hit.objectKind, 'pipe')
  96. })
  97. check('GPU instance pick resolves instanceId instead of batch first item', () => {
  98. picked = { id: { kind: 'pipe-model-batch', instances: ['pipe:P-001', 'component:V-001'] }, instanceId: 1 }
  99. const hit = engineContext.pipePicker(pickViewer, 120, 230)
  100. assert.equal(hit.kind, 'pipeline')
  101. assert.equal(hit.code, 'V-001')
  102. assert.equal(hit.objectKind, 'component')
  103. picked = { id: { kind: 'pipe-model-batch', instances: ['pipe:P-001'] }, instanceId: 7 }
  104. assert.equal(engineContext.pipePicker(pickViewer, 120, 230), null)
  105. })
  106. console.log(`T23 Three.js enhancement: ${checks}/${checks}`)