t52-device-pick.mjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. import assert from 'node:assert/strict'
  2. import * as THREE from 'three'
  3. import { planPipeEnhancement } from '../../../src/modules/cockpit/domain/pipeEnhancement.js'
  4. import { planEnhancementDevices } from '../../../src/modules/cockpit/domain/enhancementDevices.js'
  5. import { mountPipeEnhancement } from '../../../src/modules/cockpit/services/threePipeEnhancement.js'
  6. const plan = planPipeEnhancement({ selected: { kind: 'pipe', code: 'P' }, visiblePipes: [
  7. { code: 'P', industry: 'WATER_SUPPLY', diameter: 400, positions: [[110, 28], [110.001, 28]] },
  8. ] })
  9. Object.assign(plan, planEnhancementDevices(plan, [{ code: 'D', name: '真实供水设备', industry: 'WATER_SUPPLY', pointCode: 'MP', longitude: 110.0005, latitude: 28, coordinate: { renderable: true } }]))
  10. const canvas = new EventTarget()
  11. canvas.getBoundingClientRect = () => ({ left: 0, top: 0, width: 900, height: 500 })
  12. let scene, camera, picked
  13. const host = { clientWidth: 900, clientHeight: 500, appendChild(n) { n.parentNode = this }, removeChild(n) { n.parentNode = null } }
  14. const viewer = mountPipeEnhancement(host, plan, {
  15. onDevicePick: (d) => { picked = d }, rendererFactory: () => ({ domElement: canvas, setSize() {}, dispose() {}, render(s, c) { scene = s; camera = c } }),
  16. })
  17. scene.updateMatrixWorld(true)
  18. camera.updateMatrixWorld()
  19. const device = scene.getObjectByName('device:D')
  20. assert.ok(device)
  21. const icon = device.getObjectByName('device-icon')
  22. assert.ok(icon?.isSprite, '每个可显示设备必须有朝向镜头的点击图标')
  23. assert.equal(icon.material.depthTest, false, '设备图标必须在放大管体前可见')
  24. assert.ok(icon.material.map?.image?.data?.some((value) => value > 0), '设备图标必须有可渲染图案')
  25. const initialWorldSize = icon.getWorldScale(new THREE.Vector3()).y
  26. const initialDistance = camera.position.distanceTo(icon.getWorldPosition(new THREE.Vector3()))
  27. const screenSize = initialWorldSize / initialDistance
  28. assert.ok(screenSize > 0)
  29. assert.equal(viewer.focusDevice('D'), true)
  30. const focusedWorldSize = icon.getWorldScale(new THREE.Vector3()).y
  31. const focusedDistance = camera.position.distanceTo(icon.getWorldPosition(new THREE.Vector3()))
  32. assert.ok(Math.abs(focusedWorldSize / focusedDistance - screenSize) / screenSize < 0.03, '图标保持固定屏幕尺寸')
  33. const screen = icon.getWorldPosition(new THREE.Vector3()).project(camera)
  34. const clientX = (screen.x + 1) * 450
  35. const clientY = (1 - screen.y) * 250
  36. function pointer(type, x = clientX) {
  37. const event = new Event(type, { cancelable: true })
  38. Object.assign(event, { button: 0, pointerId: 1, clientX: x, clientY })
  39. canvas.dispatchEvent(event)
  40. }
  41. pointer('pointerdown'); pointer('pointerup')
  42. assert.equal(picked.code, 'D')
  43. assert.equal(picked.pointCode, 'MP')
  44. assert.equal(picked.placementText, '邻近设备 · 示意安装')
  45. picked = null
  46. const root = scene.getObjectByName('pipe-enhancement:P')
  47. const blocker = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 0.5), new THREE.MeshBasicMaterial())
  48. blocker.position.copy(root.worldToLocal(camera.position.clone().lerp(device.getWorldPosition(new THREE.Vector3()), 0.5)))
  49. root.add(blocker)
  50. root.updateMatrixWorld(true)
  51. pointer('pointerdown'); pointer('pointerup')
  52. assert.equal(picked.code, 'D', '可见的浮动设备图标即使覆盖管壁也可直接点开详情')
  53. root.remove(blocker); blocker.geometry.dispose(); blocker.material.dispose()
  54. picked = null
  55. pointer('pointerdown'); pointer('pointermove', clientX + 30); pointer('pointerup', clientX + 30)
  56. assert.equal(picked, null)
  57. viewer.destroy()
  58. pointer('pointerdown'); pointer('pointerup')
  59. assert.equal(picked, null)
  60. console.log('PASS 真实Three设备模型射线点选派发业务身份;拖拽和销毁后不误派发')