From 16cb7ce4de8c9f2640888863f1a6da55cce2d0ad Mon Sep 17 00:00:00 2001 From: zch Date: Mon, 28 Oct 2024 10:42:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(board):=20=E6=B7=BB=E5=8A=A0=E6=9C=BA?= =?UTF-8?q?=E5=9C=BA=E6=A8=A1=E5=9E=8B=E9=A1=B5=E9=9D=A2=E5=89=8D=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E4=BA=A4=E4=BA=92=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 AGV小车的参考 - 优化模型展示效果,增加标签渲染和前后端交互功能 - 修改标签不能跟随刷新、依旧重复保留的bug --- src/views/board/model/AirportModel.vue | 109 ++++-- src/views/board/参考/agv.js | 518 +++++++++++++++++++++++++ 2 files changed, 594 insertions(+), 33 deletions(-) create mode 100644 src/views/board/参考/agv.js diff --git a/src/views/board/model/AirportModel.vue b/src/views/board/model/AirportModel.vue index 04d47bf..51d8801 100644 --- a/src/views/board/model/AirportModel.vue +++ b/src/views/board/model/AirportModel.vue @@ -1,10 +1,14 @@ diff --git a/src/views/board/参考/agv.js b/src/views/board/参考/agv.js new file mode 100644 index 0000000..c85418a --- /dev/null +++ b/src/views/board/参考/agv.js @@ -0,0 +1,518 @@ +/* +import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader' +import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader' +import * as THREE from 'three' +import { camera, labelRenderer, renderer, scene, isLoading } from '@/views/board/model/setThree' +import * as TWEEN from '@tweenjs/tween.js' + +let timeOrSpeed = true + +let isAGVLoading = false + +let isAGVAnimation = { + '2AGV': false, + '3AGV': false, + '5CCAGV': false, + '5BFAGV': false, + '5CTU': false +} +let AGVanimationLine = { + '2AGV': [], + '3AGV': [], + '5CCAGV': [], + '5BFAGV': [], + '5CTU': [] +} +const calculateAngle = (sideA, sideB) => { + + // 计算角度 + let angle = Math.atan2(sideB, sideA) * 180 / Math.PI + + // 确保角度值在0到180度之间 + if (angle < 0) { + angle += 180 + } + + return angle +} + +let animationEnum = ['2AGV', '3AGV', '5CCAGV', '5BFAGV', '5CTU'] + +const AGVanimation = async(e) => { + try { + if (AGVanimationLine[e].length > 0) { + await AGVanimationLine[e][0]() + AGVanimationLine[e].shift() + if (AGVanimationLine[e].length > 0) { + AGVanimation(e) + } else { + isAGVAnimation[e] = false + } + } + } catch (e) { + console.log(e) + } +} + +function AGVanimate() { + animationEnum.forEach(e => { + if (AGVanimationLine[e].length > 0 && isAGVAnimation[e] === false) { + isAGVAnimation[e] = true + AGVanimation(e) + } + }) + requestAnimationFrame(AGVanimate) +} + +AGVanimate() + +let floorOrigin = { + floor2Data: { + x: 109000, + y: 119960 + }, + floor3Data: { + x: 39000, + y: 49460 + }, + floor5Data: { + x: 159000, + y: 150575 + } +} + +let floorData1 = { + floor2Data: { + x: 42500, + z: 20500 + }, + floor3Data: { + x: 42500, + z: 20500 + }, + floor5Data: { + x: 50300, + z: 20500 + } +} +let floorData = { + floor2Data: { + x: 1314, + z: 636 + }, + floor3Data: { + x: 1314, + z: 636 + }, + floor5Data: { + x: 1314, + z: 636 + } +} + +let agvData = { + floor2AGVGroup: null, + floor2AGV: null, + floor2AGVData: { + x: 0, + z: 0, + rotate: 0 + }, + floor2AGVGroupLocation: {}, + + floor3AGVGroup: null, + floor3AGV: null, + floor3AGVData: { + x: 0, + z: 0, + rotate: 0 + }, + floor3AGVGroupLocation: {}, + + floor5CCAGVGroup: null, + floor5CCAGV: null, + floor5CCAGVGroupLocation: {}, + floor5CCAGVData: { + x: 0, + y: 0, + rotate: 0 + }, + + floor5BFAGVGroup: null, + floor5BFAGV: null, + floor5BFAGVGroupLocation: {}, + floor5BFAGVData: { + x: 0, + y: 0, + rotate: 0 + }, + + floor5CTUGroup: null, + floor5CTU: null, + floor5CTUGroupLocation: {}, + floor5CTUData: { + x: 0, + y: 0, + rotate: 0 + } +} + +let agvLabel = { + '2AGV': null, + '2AGVData': null, + '3AGV': null, + '3AGVData': null, + '5CCAGV': null, + '5CCAGVData': null, + '5BFAGV': null, + '5BFAGVData': null, + '5CTU': null, + '5CTUData': null + +} + +let loadAGVEnum = { + 'beifushiAGV005': 'floor5BFAGV', + 'chacheshiAGV005': 'floor5CCAGV', + 'CTU005': 'floor5CTU', + 'chacheshiAGV002': 'floor2AGV', + 'chacheshiAGV003': 'floor3AGV' +} +let loadAGVGroupEnum = { + 'beifushiAGV005': 'floor5BFAGVGroup', + 'chacheshiAGV005': 'floor5CCAGVGroup', + 'CTU005': 'floor5CTUGroup', + 'chacheshiAGV002': 'floor2AGVGroup', + 'chacheshiAGV003': 'floor3AGVGroup' +} +let LoadAGVDataEnum = { + 'beifushiAGV005': 'floor5BFAGVData', + 'chacheshiAGV005': 'floor5CCAGVData', + 'CTU005': 'floor5CTUData', + 'chacheshiAGV002': 'floor2AGVData', + 'chacheshiAGV003': 'floor3AGVData' +} +let LoadAGVLocationEnum = { + 'beifushiAGV005': 'floor5BFAGVGroupLocation', + 'chacheshiAGV005': 'floor5CCAGVGroupLocation', + 'CTU005': 'floor5CTUGroupLocation', + 'chacheshiAGV002': 'floor2AGVGroupLocation', + 'chacheshiAGV003': 'floor3AGVGroupLocation' +} +let AGVLabelEnum = { + 'beifushiAGV005': '5BFAGV', + 'chacheshiAGV005': '5CCAGV', + 'CTU005': '5CTU', + 'chacheshiAGV002': '2AGV', + 'chacheshiAGV003': '3AGV' +} +let axisEnum = { + 'chacheshiAGV002': { + x: 9, + z: 15 + }, + 'chacheshiAGV003': { + x: 9, + z: 20.5 + }, + 'beifushiAGV005': { + z: 11.6 + }, + 'chacheshiAGV005': { + x: 9, + z: 22 + }, + 'CTU005': { + x: -3.5, + z: -20 + } +} + +const loadingManager = new THREE.LoadingManager() +loadingManager.onLoad = function() { + isAGVLoading = true + agvData.floor5CTUGroup.position.z = -205 + agvLabel['5CTU'].position.z = -205 + agvData.floor5CTUGroup.position.x = 100 + agvLabel['5CTU'].position.x = -205 + AGVAnimation('5CTU', '5CTU', 'x', 7) + agvData.floor5CTUGroup.rotation.y = 90 * (Math.PI / 180) +} + const loadAGV = (r) => { + r.forEach(e => { + let agvName = loadAGVEnum[e] + let agvGroupName = loadAGVGroupEnum[e] + let agvDataName = LoadAGVDataEnum[e] + let agvLocationName = LoadAGVLocationEnum[e] + let agvLabelData = AGVLabelEnum[e] + let mtlLoader = new MTLLoader() + mtlLoader.load(`/model/${e}/${e}.mtl`, + materials => { + materials.preload() + let loader = new OBJLoader(loadingManager) + loader.setMaterials(materials) + loader.load( + `/model/${e}/${e}.obj`, + object => { + object.children[0].geometry.computeBoundingBox() + let axis = new THREE.Vector3() + axis.addVectors(object.children[0].geometry.boundingBox.min, object.children[0].geometry.boundingBox.max) + axis.multiplyScalar(0.5) + axis.applyMatrix4(object.children[0].matrixWorld) + + agvData[agvGroupName] = new THREE.Group() + agvData[agvGroupName].add(object) + let axisDis = axisEnum[e] + Object.keys(axisDis || {}).forEach(v => { + axis[v] += axisDis[v] + }) + agvData[agvGroupName].position.set(axis.x, axis.y, axis.z) + agvLabel[agvLabelData].position.set(axis.x, axis.y, axis.z) + agvData[agvLocationName] = axis + object.position.set(-axis.x, -axis.y, -axis.z) + agvData[agvDataName].x = axis.x + agvData[agvDataName].z = axis.z + scene.add(agvData[agvGroupName]) + scene.add(agvLabel[agvLabelData]) + + object.name = 'tishengji_tuopan' + // scene.add(object) + + agvData[agvName] = object + // 更新渲染器 + renderer.render(scene, camera) + }, + () => { + }, + err => { + loadAGV([e]) + } + ) + }) + }) +} + +// loadAGV(['chacheshiAGV003']) + +let AGVGroupEnum = { + '5BFAGV': 'floor5BFAGVGroupLocation', + '5CCAGV': 'floor5CCAGVGroupLocation', + '5CTU': 'floor5CTUGroupLocation', + '2AGV': 'floor2AGVGroupLocation', + '3AGV': 'floor3AGVGroupLocation' +} +let AGVDataEnum = { + '5BFAGV': 'floor5BFAGVData', + '5CCAGV': 'floor5CCAGVData', + '5CTU': 'floor5CTUData', + '2AGV': 'floor2AGVData', + '3AGV': 'floor3AGVData' +} +let groupEnum = { + '5BFAGV': 'floor5BFAGVGroup', + '5CCAGV': 'floor5CCAGVGroup', + '5CTU': 'floor5CTUGroup', + '2AGV': 'floor2AGVGroup', + '3AGV': 'floor3AGVGroup' +} +let floorEnum = { + '5BFAGV': 'floor5Data', + '5CCAGV': 'floor5Data', + '5CTU': 'floor5Data', + '2AGV': 'floor2Data', + '3AGV': 'floor3Data' +} +let rotateF = (e) => { + e %= 360 + + if (e > 180) { + return e - 360 + } + if (e < -180) { + return e + 360 + } + return e +} + +const AGVAnimationF1 = (item, location, time = 2000) => { + + let AGVGroupLocationData = AGVGroupEnum[item] + let AGVLocationData = AGVDataEnum[item] + let group = groupEnum[item] + let floor = floorEnum[item] + +} + +const AGVAnimationF = (item, type, newLocation = {}, time = 2000) => { + let s = 1 + if (type === 'rotate') { + newLocation.rotate %= 360 + if ((item === '2AGV' || item === '3AGV' || item === '5CCAGV') && type === 'rotate') { + newLocation.rotate -= 90 + } + if ((item === '5CTU') && type === 'rotate') { + newLocation.rotate += 90 + } + } + let AGVGroupLocationData = AGVGroupEnum[item] + let AGVLocationData = AGVDataEnum[item] + let group = groupEnum[item] + let floor = floorEnum[item] + let agvLabelData = agvLabel?.[item] + switch (type) { + case 'xz': { + return new Promise(async(resolve, reject) => { + let locationx = agvData[AGVGroupLocationData].x - (newLocation.x / 100) * floorData[floor].x + let locationz = agvData[AGVGroupLocationData].z + (newLocation.z / 100) * floorData[floor].z + let xlength = agvData[AGVLocationData].x - locationx + let zlength = agvData[AGVLocationData].z - locationz + + let rotate = calculateAngle(Math.abs(xlength), Math.abs(zlength)) + if (xlength < 0 && zlength < 0) { + rotate = 180 - rotate + } + if (xlength < 0 && zlength > 0) { + rotate = 180 + rotate + } + if (xlength > 0 && zlength > 0) { + rotate = 360 - rotate + } + + if (xlength === 0 && zlength === 0) { + + } else { + await AGVAnimationF(item, 'rotate', { rotate: rotate }) + } + + let tween = new TWEEN.Tween(agvData[group].position) + .to({ x: locationx, z: locationz }, time) + .onComplete(() => { + agvData[group].position.x = locationx + agvData[AGVLocationData].x = locationx + agvData[group].position.z = locationz + agvData[AGVLocationData].z = locationz + resolve() + tween.stop() + tween = null + }) + .start() + + if(agvLabelData){ + let tween1 = new TWEEN.Tween(agvLabelData.position) + .to({ x: locationx, z: locationz }, time) + .onComplete(() => { + tween1.stop() + tween1 = null + }) + .start() + } + + + }) + } + case 'x': { + return new Promise(async(resolve, reject) => { + let location = agvData[AGVGroupLocationData].x - (newLocation.x / 100) * floorData[floor].x + if ((agvData[group].position.x - location) > 0) { + await AGVAnimationF(item, 'rotate', { rotate: 0 }) + } else { + await AGVAnimationF(item, 'rotate', { rotate: 180 }) + } + if (agvData[AGVLocationData].x === location) { + resolve() + } + let distance = location - agvData[AGVLocationData].x + let tween = new TWEEN.Tween(agvData[group].position) + .to({ x: location }, Math.abs(distance) / s * 16) // 移动到(1, 1, 1),持续1000毫秒 + .onComplete(() => { + agvData[group].position.x = location + agvData[AGVLocationData].x = location + resolve() + tween.stop() + tween = null + }) + .start() + + if(agvLabelData){ + let tween1 = new TWEEN.Tween(agvLabelData.position) + .to({ x: location }, time) + .onComplete(() => { + tween1.stop() + tween1 = null + }) + .start() + } + }) + } + case 'z': { + return new Promise(async(resolve, reject) => { + let location = agvData[AGVGroupLocationData].z + (newLocation.z / 100) * floorData[floor].z + if ((agvData[group].position.z - location) > 0) { + await AGVAnimationF(item, 'rotate', { rotate: 270 }) + } else { + await AGVAnimationF(item, 'rotate', { rotate: 90 }) + } + if (agvData[AGVLocationData].z === location) { + resolve() + } + let distance = location - agvData[AGVLocationData].z + + let tween = new TWEEN.Tween(agvData[group].position) + .to({ z: location }, Math.abs(distance) / s * 16) // 移动到(1, 1, 1),持续1000毫秒 + .onComplete(() => { + agvData[group].position.z = location + agvData[AGVLocationData].z = location + resolve() + tween.stop() + tween = null + }) + .start() + }) + } + case 'rotate': { + return new Promise((resolve, reject) => { + let nowRotate = parseFloat((agvData[group].rotation.y / (Math.PI / 180)).toFixed(2)) + nowRotate = rotateF(nowRotate) + newLocation.rotate = rotateF(newLocation.rotate) + let distance = newLocation.rotate - nowRotate + distance = rotateF(distance) + let nowRotateNum = agvData[group].rotation.y + let endRotateNum = nowRotateNum + (distance * (Math.PI / 180)) + let bool = (nowRotateNum - endRotateNum) > 0 + + let tween = new TWEEN.Tween(agvData[group].rotation) + .to({ y: newLocation.rotate * (Math.PI / 180) }, 500) // 移动到(1, 1, 1),持续1000毫秒 + .onComplete(() => { + agvData[group].rotation.y = (newLocation.rotate * (Math.PI / 180)) + resolve() + tween.stop() + tween = null + }) + .start() + + }) + } + default : { + return new Promise((resolve, reject) => { + resolve() + }) + } + } + +} +const AGVAnimation = (e, item, type, newLocation = {}, time = 2000) => { + if (agvData[groupEnum[e]] && AGVanimationLine[e].length < 10) { + AGVanimationLine[e].push(() => AGVAnimationF(item, type, newLocation, time)) + } +} + +export { + agvData, + AGVAnimation, + floorOrigin, + floorData1, + isAGVLoading, + agvLabel, + loadAGV +} +*/