diff --git a/package.json b/package.json index b132920..507f9be 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "quill": "1.3.7", "screenfull": "5.0.2", "sortablejs": "1.10.2", + "three": "^0.169.0", "vue": "2.6.12", "vue-count-to": "1.0.13", "vue-cropper": "0.5.5", diff --git a/src/views/board/agv.js b/src/views/board/agv.js new file mode 100644 index 0000000..7b38aba --- /dev/null +++ b/src/views/board/agv.js @@ -0,0 +1,516 @@ +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 +} diff --git a/src/views/board/animation.js b/src/views/board/animation.js new file mode 100644 index 0000000..802f30f --- /dev/null +++ b/src/views/board/animation.js @@ -0,0 +1,201 @@ +import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader' +import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader' +import { + isLoading, + tuopanLocation, + tishengjiLocation, keyidongModel, storeyHeight, tuopanModel, animationLine, storeyWidth +} from './setThree' +import * as TWEEN from '@tweenjs/tween.js' +import { agvData } from '@/views/board/model/agv' + +let tuopanInishengji = true + +let s = 1 + +const tishengjiAnimation = (model, floor, newLocation = 0, time = 2000) => { + + return new Promise((resolve, reject) => { + tishengjiLocation.floor = floor + if (!model || tishengjiLocation.y === newLocation) { + resolve() + } + let distance = newLocation - tishengjiLocation.y + + let tween = new TWEEN.Tween(model.position) + .to({ y: newLocation }, Math.abs(distance) / s * 16) // 移动到(1, 1, 1),持续1000毫秒 + .onComplete(() => { + model.position.y = newLocation + tishengjiLocation.y = newLocation + resolve() + tween.stop() + tween = null + }) + .start() + + // let time1 = setInterval(() => { + // if ((distance > 0 && model.position.y >= newLocation) || (distance < 0 && model.position.y <= newLocation)) { + // model.position.y = newLocation + // tishengjiLocation.y = newLocation + // resolve() + // clearInterval(time1) + // clearTimeout(time3) + // } else { + // model.position.y += distance / (s / 16) + // tishengjiLocation.y = model.position.y + // } + // }, 16) + // let time3 = setTimeout(() => { + // model.position.y = newLocation + // tishengjiLocation.y = newLocation + // resolve() + // clearInterval(time1) + // clearTimeout(time3) + // }, time + 1000) + + }) +} + +const tuopanXAnimation = (model, newLocation = 0, time = 2000) => { + return new Promise((resolve, reject) => { + if (!model || newLocation === tuopanLocation.x || tishengjiLocation.floor !== tuopanLocation.floor) { + resolve() + } else { + tuopanInishengji = newLocation === 0 + + let distance = newLocation - tuopanLocation.x + let tween = new TWEEN.Tween(model.position) + .to({ x: newLocation }, Math.abs(distance) / s * 16) // 移动到(1, 1, 1),持续1000毫秒 + .onComplete(() => { + model.position.x = newLocation + tuopanLocation.x = newLocation + resolve() + tween.stop() + tween = null + }) + .start() + + // let time1 = setInterval(() => { + // if ((distance > 0 && model.position.x >= newLocation) || (distance < 0 && model.position.x <= newLocation)) { + // model.position.x = newLocation + // tuopanLocation.x = newLocation + // resolve() + // clearInterval(time1) + // clearTimeout(time3) + // } else { + // model.position.x += distance / (time / 16) + // tuopanLocation.x = model.position.x + // } + // }, 16) + // let time3 = setTimeout(() => { + // model.position.x = newLocation + // tuopanLocation.x = newLocation + // resolve(0) + // clearInterval(time1) + // clearTimeout(time3) + // }, time + 1000) + } + }) +} + +const tuopanYAnimation = (model, floor, newLocation = 0, time = 2000) => { + return new Promise((resolve, reject) => { + tuopanLocation.floor = floor + if (!model || tuopanLocation.y === newLocation) { + resolve() + } + let distance = newLocation - tuopanLocation.y + let tween = new TWEEN.Tween(model.position) + .to({ y: newLocation }, Math.abs(distance) / s * 16) // 移动到(1, 1, 1),持续1000毫秒 + .onComplete(() => { + model.position.y = newLocation + tuopanLocation.y = newLocation + resolve() + tween.stop() + tween = null + }) + .start() + + // let time1 = setInterval(() => { + // if ((distance > 0 && model.position.y >= newLocation) || (distance < 0 && model.position.y <= newLocation)) { + // model.position.y = newLocation + // tuopanLocation.y = newLocation + // resolve() + // clearInterval(time1) + // clearTimeout(time3) + // } else { + // model.position.y += distance / (time / 16) + // tuopanLocation.y = model.position.y + // } + // }, 16) + // let time3 = setTimeout(() => { + // model.position.y = newLocation + // tuopanLocation.y = newLocation + // resolve() + // clearInterval(time1) + // clearTimeout(time3) + // }, time + 1000) + }) +} + +const addtuopan = (params) => { + return new Promise((resolve, reject) => { + try { + let objLoader1 = new OBJLoader() + let mtlLoader1 = new MTLLoader() + let objLoader2 = new OBJLoader() + let mtlLoader2 = new MTLLoader() + mtlLoader1.load('/model/jiaodongjichang.mtl', function(materials) { + materials.preload() + objLoader1.setMaterials(materials) + objLoader1.load('/model/jiaodongjichang.obj.obj', function(object1) { + // 将模型添加到场景 + params.scene.add(object1) + + // mtlLoader2.load('/model/tishengji/tishengji.mtl', function(materials2) { + // materials2.preload() + // objLoader2.setMaterials(materials2) + // objLoader2.load('/model/tishengji/tishengji.obj', function(object2) { + // 将模型添加到场景 + // object1.add(object2) + resolve(object1) + // }) + // }) + + }) + }) + } catch (e) { + resolve() + } + }) + +} + +setInterval(() => { + if (isLoading) { + + if (animationLine.length < 5) { + + let floor = Math.ceil(Math.random() * 5) + + animationLine.push(() => { + if (tuopanInishengji) { + return Promise.all([tishengjiAnimation(keyidongModel, floor, storeyHeight[floor]), tuopanYAnimation(tuopanModel, floor, storeyHeight[floor])]) + } else { + animationLine.push(() => tishengjiAnimation(keyidongModel, floor, storeyHeight[floor])) + } + }) + if (Math.random() > 0.5) { + animationLine.push(() => tuopanXAnimation(tuopanModel, storeyWidth[floor])) + animationLine.push(() => tuopanXAnimation(tuopanModel, 0)) + } + } + } +}, 1800) + +export { + tuopanInishengji, + tishengjiAnimation, + tuopanXAnimation, + tuopanYAnimation, + addtuopan +}