| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- import assert from 'node:assert/strict'
- import * as THREE from 'three'
- import { planPipeEnhancement } from '../../../src/modules/cockpit/domain/pipeEnhancement.js'
- import { mountPipeEnhancement } from '../../../src/modules/cockpit/services/threePipeEnhancement.js'
- const source = { code: 'hollow-pipe', industry: 'WATER_SUPPLY', diameter: 400, material: 'PE', positions: [[110, 28], [110.001, 28]] }
- const original = JSON.stringify(source)
- const plan = planPipeEnhancement({ selected: { kind: 'pipe', code: source.code }, visiblePipes: [source] })
- class Canvas extends EventTarget {}
- const host = { clientWidth: 1000, clientHeight: 600,
- ownerDocument: { createElement: () => new Canvas() }, children: [],
- appendChild(node) { node.parentNode = this; this.children.push(node) },
- removeChild(node) { this.children.splice(this.children.indexOf(node), 1); node.parentNode = null } }
- let scene
- const viewer = mountPipeEnhancement(host, plan, { rendererFactory: ({ canvas }) => ({
- domElement: canvas, setPixelRatio() {}, setSize() {}, render(value) { scene = value; scene.updateMatrixWorld(true) }, dispose() {},
- }) })
- const root = scene.children.find((n) => n.name === `pipe-enhancement:${source.code}`)
- const meshes = []
- root.traverse((node) => { if (node.isMesh) meshes.push(node) })
- const bounds = new THREE.Box3().setFromObject(root)
- const ray = new THREE.Raycaster(new THREE.Vector3(bounds.min.x - 1, 0, 0), new THREE.Vector3(1, 0, 0))
- let failures = 0
- let passes = 0
- function check(name, action) {
- try { action(); passes += 1; console.log(`PASS ${name}`) }
- catch (error) { failures += 1; console.error(`FAIL ${name}: ${error.message}`) }
- }
- check('轴向穿过中空管腔,不被端盖封死', () => {
- assert.equal(ray.intersectObject(root, true).length, 0)
- ray.set(new THREE.Vector3(bounds.getCenter(new THREE.Vector3()).x, 2, 0), new THREE.Vector3(0, -1, 0))
- assert.ok(ray.intersectObject(root, true).length > 0, '管壁须有实体表面')
- })
- check('端部有法兰与一圈螺栓,沿管轴附着', () => {
- const flanges = meshes.filter((m) => m.name === 'pipe-flange')
- const bolts = meshes.filter((m) => m.name === 'pipe-bolt')
- assert.equal(flanges.length, 2)
- assert.equal(bolts.length, 16)
- for (const flange of flanges) {
- const p = flange.getWorldPosition(new THREE.Vector3())
- assert.ok(Math.abs(p.y) < 1e-7 && Math.abs(p.z) < 1e-7)
- }
- })
- check('有分节接缝,没有独立定位环', () => {
- assert.ok(meshes.some((m) => m.name === 'pipe-seam'))
- assert.ok(!meshes.some((m) => m.geometry.type === 'TorusGeometry'), '旧中心定位环必须移除')
- })
- check('管体保持供水行业色,不改源路径与示意说明', () => {
- const body = meshes.find((m) => m.name === 'pipe-wall')
- assert.ok(body)
- assert.equal(body.material.color.getHexString(), '168bff')
- assert.equal(JSON.stringify(source), original)
- assert.match(plan.disclaimer, /不代表工程竣工模型/)
- })
- function trackResources(model) {
- const resources = new Map()
- model.traverse((node) => {
- for (const resource of [node.geometry, ...[].concat(node.material || [])].filter(Boolean)) {
- if (resources.has(resource)) continue
- resources.set(resource, 0)
- resource.addEventListener('dispose', () => resources.set(resource, resources.get(resource) + 1))
- }
- })
- return () => {
- assert.ok(resources.size > 0)
- for (const count of resources.values()) assert.ok(count > 0, '每个几何与材质都须释放')
- }
- }
- const assertInitialReleased = trackResources(root)
- viewer.sync({ ...plan, object: { ...plan.object, code: 'replacement' } })
- check('换选移除旧模型并释放全部几何与材质', () => {
- assert.equal(root.parent, null)
- assertInitialReleased()
- })
- check('短管段保持真实长径比例,不为可见性拉长或加粗', () => {
- const tiny = { ...plan, object: { ...plan.object, path: [[110, 28], [110.00001, 28]], position: [110, 28] } }
- viewer.sync(tiny)
- const next = scene.children.find((n) => n.name === `pipe-enhancement:${source.code}`)
- const box = new THREE.Box3().setFromObject(next)
- const size = box.getSize(new THREE.Vector3())
- assert.ok(size.x > 0.98 && size.x < 1.02)
- assert.ok(size.y > 0.4 && size.y < 0.6)
- assert.ok(box.getCenter(new THREE.Vector3()).length() < 1e-6)
- })
- check('纵向管段法兰随路径定向,轴向管腔不封死', () => {
- viewer.sync({ ...plan, object: { ...plan.object, path: [[110, 28], [110, 28.001]] } })
- const next = scene.children.find((n) => n.name === `pipe-enhancement:${source.code}`)
- const box = new THREE.Box3().setFromObject(next)
- ray.set(new THREE.Vector3(0, 0, box.min.z - 1), new THREE.Vector3(0, 0, 1))
- assert.equal(ray.intersectObject(next, true).length, 0)
- })
- check('折线共享拐角不堆叠法兰,仅真实两端有法兰', () => {
- viewer.sync({ ...plan, object: { ...plan.object, path: [[110, 28], [110.001, 28], [110.001, 28.001]] } })
- const next = scene.children.find((n) => n.name === `pipe-enhancement:${source.code}`)
- const flanges = []
- next.traverse((n) => { if (n.name === 'pipe-flange') flanges.push(n) })
- assert.equal(flanges.length, 2, '折点不能额外生成一对法兰')
- })
- check('关闭释放当前模型全部几何材质、移除画布并可重复销毁', () => {
- const currentRoot = scene.children.find((n) => n.name === `pipe-enhancement:${source.code}`)
- const assertCurrentReleased = trackResources(currentRoot)
- viewer.destroy()
- assertCurrentReleased()
- viewer.destroy()
- assert.equal(host.children.length, 0)
- assert.equal(viewer.getState().status, 'destroyed')
- })
- console.log(`T50 pipe detail: ${passes} passed, ${failures} failed`)
- if (failures) process.exitCode = 1
|