diff --git a/src/editor/propEditors/PtrController.vue b/src/editor/propEditors/PtrController.vue index 423b11c..ebe10da 100644 --- a/src/editor/propEditors/PtrController.vue +++ b/src/editor/propEditors/PtrController.vue @@ -6,7 +6,7 @@ import { CopyDocument, Delete } from "@element-plus/icons-vue"; import { Typeof } from "@ease-forge/shared"; import type Viewport from "@/core/engine/Viewport"; import {BayEditorProps} from "@/editor/propEditors/BayEditor.vue"; -import Cl23dObject from "@/modules/cl2/Cl23dObject"; +import PtrObject from "@/modules/amr/ptr/PtrObject"; defineOptions({ name: 'PtrController', @@ -18,9 +18,16 @@ function start() { const viewport: Viewport = window['viewport'] // viewport.envManager.client.publish('/agv_robot/status', '') const item = viewport.state.selectedItem - const object3D: Cl23dObject = viewport.state.selectedObject as Cl23dObject - console.log(item, object3D) - object3D.boot() + const ptrObject: PtrObject = viewport.state.selectedObject as PtrObject + ptrObject.boot() +} + +function stop() { + const viewport: Viewport = window['viewport'] + // viewport.envManager.client.publish('/agv_robot/status', '') + const item = viewport.state.selectedItem + const ptrObject: PtrObject = viewport.state.selectedObject as PtrObject + ptrObject.shutdown() } @@ -29,6 +36,7 @@ function start() { diff --git a/src/modules/amr/ptr/PtrObject.ts b/src/modules/amr/ptr/PtrObject.ts new file mode 100644 index 0000000..59a90c6 --- /dev/null +++ b/src/modules/amr/ptr/PtrObject.ts @@ -0,0 +1,828 @@ +import * as THREE from "three"; +import { + AmrErrorCode, + AmrMsg, AmrMsg10010, AmrMsg10050, AmrMsg10060, AmrMsg10110, AmrMsg10120, AmrMsg20010, + AmrMsg20011, AmrMsg20020, AmrMsg20050, AmrMsg20060, AmrMsg20100, + AmrMsg20147, + AmrMsg20148, AmrMsg20149, AmrMsg20150, AmrMsg20250, + type CEventId, + type COperationType, + type CPickMode, + type CTaskMode, CurBatteryData, + type LogicDirection, + TaskCompletedData, + TaskModeChangeData, + TaskStatusChangeData, + TaskTypeChangeData +} from "@/core/manager/amr/AmrMessageDefine"; +import {worldModel} from "@/core/manager/WorldModel"; +import Viewport from "@/core/engine/Viewport"; +import {Euler} from "three/src/math/Euler"; +import gsap from "gsap"; + + +type CStepTaskType = "MOVE" | "MOVE_BACKWARD" | "ROTATION" | "LOAD" | "UNLOAD" | "CHARGE" + +export interface StepTask { + SeqNo: number; + StepTaskType: CStepTaskType; + OperationType: 0 | 1 | 2 | 3 | 4 | 5 | 135 | 136; + PickMode: 0 | 1 | 2 | 3 | 4 | 5 | 6; + X: number; + Y: number; + Speed: number; + EndDirection: 0 | 1 | 2 | 3 | 15; + ChargeLocation: number; + GoodsSlotHeight: number; + position: THREE.Vector3; + isCompleted: boolean; +} + +export default class PtrObject extends THREE.Object3D { + + public item: ItemJson + public readonly viewport: Viewport + + public currentLogicX: number = -1 + public currentLogicY: number = -1 + public currentDirection: LogicDirection = 15 + public currentOrientation: number = 0 + public sendMessageQueue: AmrMsg[] = [] + + public Battery: number = 100 + + public vehicleId: number + + public clock = new THREE.Clock() + + override AGVModel = "" + override AGVFnModel = "" + + private currentStepTaskList: StepTask[] = [] + private runningStepTask: StepTask = null + public runningStepTaskList: StepTask[] = [] + + public travelAnimation: core.Tween = null + + // 心跳间隔 UInt32 单位: s + private heartBeatInterval: number = 0 + // 小车所有上报消息重试间隔(未收到应答消息时重发消息) UInt32 单位: s + private mqRetryInterval: number = 3 + + private __TaskMode: CTaskMode = 0 + private __OperationType: COperationType = 0 + private __TaskStatus: CEventId = 0 + private __PickMode: CPickMode = 0 + + + get TaskMode(): CTaskMode { + return this.__TaskMode + } + + set TaskMode(value: CTaskMode) { + if (this.__TaskMode != value) { + const info = new TaskModeChangeData(this.__TaskMode, value) + const msg = new AmrMsg20011(this.vehicleId, info) + msg.TaskMode = value + this.send20011(msg) + } + this.__TaskMode = value + } + + get OperationType(): COperationType { + return this.__OperationType + } + + set OperationType(value: COperationType) { + if (this.__OperationType != value) { + const info = new TaskTypeChangeData(this.__OperationType, value) + const msg = new AmrMsg20011(this.vehicleId, info) + msg.TaskMode = this.TaskMode + this.send20011(msg) + } + this.__OperationType = value + } + + get TaskStatus(): CEventId { + return this.__TaskStatus + } + + set TaskStatus(value: CEventId) { + if (this.__TaskStatus != value) { + + if (value != 1 && value != 4 && value != 8) { + const info = new TaskStatusChangeData(this.OperationType) + const msg = new AmrMsg20011(this.vehicleId, info) + msg.TaskMode = this.TaskMode + msg.EventId = value + this.send20011(msg) + } else if (value == 4) { + + const info = new TaskCompletedData(this.OperationType) + info.Battery = this.Battery + info.OperationResult = 0 + info.CurLogicX = this.currentLogicX + info.CurLogicY = this.currentLogicY + info.CurX = this.currentLogicX + info.CurY = this.currentLogicY + info.CurDirection = this.currentDirection + info.CurOrientation = this.currentOrientation + const msg = new AmrMsg20011(this.vehicleId, info) + msg.TaskMode = this.TaskMode + this.send20011(msg) + } + + } + } + + get PickMode(): CPickMode { + return this.__PickMode + } + set PickMode(value: CPickMode) { + if (this.__PickMode !== value) { + this.__PickMode = value + } + } + + private bootTime: number = 0 + + private heartBeatTimeCount: number = 0 + private mqRetryTimeCount: number = 0 + + constructor(item: ItemJson, viewport: Viewport) { + super(); + this.viewport = viewport + this.item = item + this.vehicleId = parseInt(item.id) + + this.viewport.addFrameTimerCallback(this.item.id, this.onFrameTimer.bind(this)) + if (!worldModel.state.runState.isVirtual) { + this.subscribeMessage('/wcs_server/' + this.item.id) + this.subscribeMessage('/agv_robot/status') + } + } + + protected onFrameTimer() { + if (!worldModel.state.runState.isVirtual) { + return + } + const delta = this.clock.getDelta() + this.mqRetryTimeCount += delta + if (this.mqRetryInterval > 0 && this.mqRetryTimeCount >= this.mqRetryInterval) { + this.mqRetryTimeCount = 0 + // 在此处理消息重试 + if (this.sendMessageQueue.length > 0) { + this.sendMessage(this.sendMessageQueue[0]) + } + } + this.heartBeatTimeCount += delta + if (this.heartBeatInterval > 0 && this.heartBeatTimeCount >= this.heartBeatInterval) { + this.heartBeatTimeCount = 0 + // 在此处发送心跳报文 + this.sendHeartBeat() + } + } + + // 开机 + boot() { + this.bootTime = Date.now(); + this.computeLogicXYAndDirection(); + + if (!worldModel.state.runState.isVirtual) { + return + } + this.subscribeMessage('/wcs_server/' + this.vehicleId) + this.send20147() + setTimeout(() => { + this.send20149() + this.TaskMode = 1 + // 检查当前所在位置和方向 根据车当前所在的xz坐标获取地标 + setTimeout(() => { + this.sendCurrentPositionAndDirection() + setTimeout(() => { + this.send20150() + }, 1000) + }, 1000) + }, 2000) + + } + + // 关机 + shutdown() { + if (!worldModel.state.runState.isVirtual) { + return + } + const content = new AmrMsg20148(this.vehicleId) + // 电量 + content.Battery = 100 + content.CreateMonoTime = Date.now() - this.bootTime + content.Uptime = content.CreateMonoTime + const m20148 = new AmrMsg(content) + this.sendMessage(m20148) + } + + // 开机上报 + send20147() { + const content = new AmrMsg20147(this.vehicleId) + content.AGVModel = this.AGVModel + content.AGVFnModel = this.AGVFnModel + // 电量 + content.Battery = 100 + content.CreateMonoTime = Date.now() - this.bootTime + const m20147 = new AmrMsg(content) + this.sendMessage(m20147) + } + + // 主程序启动上报 + send20149() { + const content = new AmrMsg20149(this.vehicleId) + content.AGVModel = this.AGVModel + content.AGVFnModel = this.AGVFnModel + // 电量 + content.Battery = 100 + content.CreateMonoTime = Date.now() - this.bootTime + const m20149 = new AmrMsg(content) + this.sendMessage(m20149) + } + + // 上线上报 + send20150() { + const content = new AmrMsg20150(this.vehicleId) + content.AGVModel = this.AGVModel + content.AGVFnModel = this.AGVFnModel + content.Battery = 100 + content.CreateMonoTime = Date.now() - this.bootTime + const m20150 = new AmrMsg(content) + this.sendMessage(m20150) + } + + // 上报当前位姿,地标和方向 + sendCurrentPositionAndDirection() { + if (this.currentLogicX <= 0 || this.currentLogicY <= 0) { + // 当前车辆所在位置未找到 + const content = new AmrMsg20250(this.vehicleId) + content.Duration = 0 + content.ErrCode = 5 + content.ErrCodeName = AmrErrorCode[5].ErrCodeName + content.ErrEvtType = 1 + content.ErrLevel = 14 + content.ErrLifecycle = 2 + content.CreateMonoTime = Date.now() - this.bootTime + const m20250 = new AmrMsg(content) + this.sendMessage(m20250) + } else { + // 发送正常地标信息 + const content = new AmrMsg20020(this.vehicleId) + content.CurDirection = this.currentDirection + content.CurOrientation = this.currentOrientation + content.CurLogicX = this.currentLogicX + content.CurLogicY = this.currentLogicY + content.CurX = this.currentLogicX + content.CurY = this.currentLogicY + content.CreateMonoTime = Date.now() - this.bootTime + const m20020 = new AmrMsg(content) + this.sendMessage(m20020) + } + } + + send20010() { + const content: AmrMsg20010 = new AmrMsg20010(this.vehicleId) + content.CurX = this.currentLogicX + content.CurY = this.currentLogicY + content.CurDirection = this.currentDirection + content.OperationType = this.OperationType + content.Battery = this.Battery + content.OperationResult = 0 + content.Summary = { + ActuatorsData: [ + { + MechNo: 1, + Name: "Mech1", + PickMode: this.PickMode + } + ] + } + + + const m20010 = new AmrMsg(content) + this.sendMessage(m20010) + } + + send20011(content: AmrMsg20011) { + const m20011 = new AmrMsg>(content) + this.sendMessage(m20011) + } + + send20020(content: AmrMsg20020) { + const m20020 = new AmrMsg(content) + this.sendMessage(m20020) + } + + send20060() { + const content = new AmrMsg20060(this.vehicleId) + content.CreateMonoTime = Date.now() - this.bootTime + content.CurBattery = new CurBatteryData() + content.CurLogicX = this.currentLogicX + content.CurLogicY = this.currentLogicY + content.CurOrientation = this.currentOrientation + content.CurX = this.position.x; + content.CurY = this.position.z; + content.X = this.position.x; + content.Y = this.position.z; + const m20060 = new AmrMsg(content) + this.sendMessage(m20060) + } + + send20250(content: AmrMsg20250) { + this.sendMessage(new AmrMsg(content)) + } + + subscribeMessage(topic: string) { + worldModel.envManager.client.subscribe(topic, {qos: 0}) + } + + sendMessage(msg: AmrMsg) { + if (!worldModel.state.runState.isVirtual) { + return + } + console.log('send message:', JSON.stringify(msg)) + if (this.sendMessageQueue.indexOf(msg) < 0) { + this.sendMessageQueue.push(msg) + } + if (this.sendMessageQueue.length <= 0) { + this.mqRetryTimeCount = 0 + } + worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(msg)) + this.heartBeatTimeCount = 0 + } + + sendHeartBeat() { + if (!worldModel.state.runState.isVirtual) { + return + } + const content = new AmrMsg20100(this.vehicleId) + content.Temperature = {Battery: this.Battery} + const m20100 = new AmrMsg(content) + worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(m20100)) + } + + sendAck(seqNo: number, vehicleId: number) { + if (!worldModel.state.runState.isVirtual) { + return + } + const msg20050 = new AmrMsg20050(seqNo, vehicleId) + const ack = new AmrMsg(msg20050) + this.heartBeatTimeCount = 0 + worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(ack)) + } + + /*==========RCS消息处理============*/ + // 处理任务 + handle10010Message(data: AmrMsg10010) { + if (this.currentStepTaskList.length > 0) { + if (this.runningStepTask.OperationType == 0 && this.runningStepTask.X == data.StartX && this.runningStepTask.Y == data.StartY) { + // this.currentStepTaskList = [] + this.makeStepTask(data) + this.executeTask() + } else { + // 此处应该有错误处理 + } + } else { + this.makeStepTask(data) + this.executeTask() + } + } + + handle10050Message(data: AmrMsg) { + if (this.sendMessageQueue.length > 0 && data.content.SeqNo === this.sendMessageQueue[0].content.SeqNo) { + this.mqRetryTimeCount = 0 + this.sendMessageQueue.shift() + } + } + + handle10060Message(data: AmrMsg) { + this.mqRetryInterval = data.content.MqRetryTime + this.heartBeatInterval = data.content.HeartBeat + this.TaskMode = 0 + } + + // 处理状态查询 + handle10110Message(data: AmrMsg) { + this.send20060() + } + + // 取消任务 + handle10120Message(data: AmrMsg) { + + } + + + /*==========真车消息处理============*/ + + // 计算逻辑方向 + computeLogicXYAndDirection() { + let ra = this.rotation.y + while (ra > Math.PI * 2) { + ra -= Math.PI * 2 + } + while (ra < 0) { + ra += Math.PI * 2 + } + const ddra = Math.PI / 8 + if (ra >= ddra * 7 || ra < ddra) { + this.currentDirection = 0; + + } else if (ra >= ddra && ra < ddra * 3) { + this.currentDirection = 3; + + } else if (ra >= ddra * 3 && ra < ddra * 5) { + this.currentDirection = 2; + + } else if (ra >= ddra * 5 && ra < ddra * 7) { + this.currentDirection = 1; + } else { + this.currentDirection = 15; + } + + const pointItem = Model.getItemByXYZ(this.position.x, this.position.y, this.position.z) + if (!pointItem || !pointItem.logicX || !pointItem.logicY) { + this.currentLogicX = -1; + this.currentLogicY = -1; + } else { + this.currentLogicX = pointItem.logicX; + this.currentLogicY = pointItem.logicY; + } + this.currentOrientation = this.getAmrOrientation(this.rotation.y) + } + + makeStepTask(data: AmrMsg10010) { + + let currentStepTask: StepTask = this.runningStepTask + if (currentStepTask == null) { + + currentStepTask = { + SeqNo: 0, + StepTaskType: "MOVE", + OperationType: 0, + PickMode: 0, + X: this.currentLogicX, + Y: this.currentLogicY, + Speed: 1000, + EndDirection: this.currentDirection, + ChargeLocation: 0, + GoodsSlotHeight: 0, + position: this.position, + isCompleted: true + } + } + let endDirection = currentStepTask.EndDirection + + if (data.Link.length > 0) { + for (let i = 0; i < data.Link.length; i++) { + const link = data.Link[i] + if ((currentStepTask.X == link.X && currentStepTask.Y == link.Y) + || (currentStepTask.X != link.X && currentStepTask.Y != link.Y)) { + continue + } else if (currentStepTask.X < link.X) { + if (link.Speed > 0) { + endDirection = 0 + } else { + endDirection = 2 + } + } else if (currentStepTask.X > link.X) { + if (link.Speed > 0) { + endDirection = 2 + } else { + endDirection = 0 + } + } else if (currentStepTask.Y < link.Y) { + if (link.Speed > 0) { + endDirection = 1 + } else { + endDirection = 3 + } + } else if (currentStepTask.Y > link.Y) { + if (link.Speed > 0) { + endDirection = 3 + } else { + endDirection = 1 + } + } + + if (endDirection != currentStepTask.EndDirection) { + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: "ROTATION", + OperationType: 0, + PickMode: 0, + X: link.X, + Y: link.Y, + Speed: link.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(link.X, link.Y) as THREE.Vector3, + isCompleted: false + } + currentStepTask = stepTask + this.currentStepTaskList.push(stepTask) + } + + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: link.Speed > 0 ? "MOVE" : "MOVE_BACKWARD", + OperationType: 0, + PickMode: 0, + X: link.X, + Y: link.Y, + Speed: link.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(link.X, link.Y) as THREE.Vector3, + isCompleted: false + } + currentStepTask = stepTask + this.currentStepTaskList.push(stepTask) + } + } + + + if (data.OperationType == 0 && data.EndDirection >= 0 && data.EndDirection <= 3) { + endDirection = data.EndDirection; + } else if (data.OperationType == 3 && data.ChargeDirection >= 0 && data.ChargeDirection <= 3) { + endDirection = data.ChargeDirection + } else if (data.OperationType == 4 && data.GoodsSlotDirection >= 0 && data.GoodsSlotDirection <= 3) { + if (data.GoodsSlotDirection == 0) { + endDirection = 3 + } else { + endDirection = (data.GoodsSlotDirection - 1) as LogicDirection + } + } + if (endDirection != currentStepTask.EndDirection) { + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: "ROTATION", + OperationType: 0, + PickMode: 0, + X: data.EndX, + Y: data.EndY, + Speed: currentStepTask.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, + isCompleted: false + } + this.currentStepTaskList.push(stepTask) + } + if (data.OperationType == 3) { + + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: "CHARGE", + OperationType: 3, + PickMode: 0, + X: data.EndX, + Y: data.EndY, + Speed: currentStepTask.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, + isCompleted: false + } + this.currentStepTaskList.push(stepTask) + + } else if (data.OperationType == 4) { + + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: data.PickMode == 1 ? "LOAD" : "UNLOAD", + OperationType: 4, + PickMode: data.PickMode, + X: data.EndX, + Y: data.EndY, + Speed: currentStepTask.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, + isCompleted: false + } + this.currentStepTaskList.push(stepTask) + + } else { + + } + + } + + executeTask() { + this.TaskMode = 2 + while (this.currentStepTaskList.length > 0) { + const stepTask = this.currentStepTaskList[0] + if (this.runningStepTask) { + if ((stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD") + && stepTask.EndDirection == this.runningStepTask.EndDirection + && (stepTask.Speed > 0) == (this.runningStepTask.Speed > 0)) { + this.runningStepTask = stepTask + this.currentStepTaskList.shift() + this.runningStepTaskList.push(stepTask) + + this.addTravel(stepTask.X, stepTask.Y, stepTask.Speed/1000) + + } else { + break + } + } else { + this.runningStepTask = stepTask + this.currentStepTaskList.shift() + this.runningStepTaskList.push(stepTask) + if (stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD") { + this.addTravel(stepTask.X, stepTask.Y, stepTask.Speed/1000) + } else if (stepTask.StepTaskType == "ROTATION") { + this.addRotation(stepTask.EndDirection) + } else if (stepTask.StepTaskType == "LOAD") { + this.addLoad(stepTask.GoodsSlotHeight/1000) + } else if (stepTask.StepTaskType == "UNLOAD") { + this.addUnload(stepTask.GoodsSlotHeight/1000) + } + } + } + } + + onActionCompleted() { + this.runningStepTaskList = [] + this.runningStepTask = null + this.computeLogicXYAndDirection() + // 当前所有动作执行完毕 + if (this.currentStepTaskList.length <= 0) { + this.send20010() + } + this.PickMode = 0 + this.OperationType = 0 + this.TaskMode = 0 + this.executeTask() + } + + /*==========动作处理============*/ + // 转 + addRotation(direction: number): Promise { + let rad = 0 + switch (direction) { + case 1: + rad = Math.PI / 2 * 3 + break + case 2: + rad = Math.PI + break + case 3: + rad = Math.PI / 2 + break + default: + rad = 0 + } + const quat1 = new THREE.Quaternion().setFromEuler(this.rotation) + const euler: Euler = new Euler(this.rotation.x, rad, this.rotation.z) + const quat2 = new THREE.Quaternion().setFromEuler(euler) + const angleDiff = quat1.angleTo(quat2) + console.log(rad, this.rotation.y, angleDiff) + const tr = this.rotation.y + angleDiff + let time = Math.abs(angleDiff) / (Math.PI / 7) + const duration = time + + return new Promise(resolve => { + gsap.to(this.rotation, { + y: tr, + duration, + ease: 'none', + onComplete: ()=>{ + resolve() + this.onActionCompleted() + } + }) + }) + } + + // 走 + addTravel(logicX: number, logicY: number, speed: number = 1): Promise { + this.OperationType = 0 + this.PickMode = 0 + const pos = Model.getPositionByLogicXY(logicX, logicY) + + const fromPos = this.position + const toPos = pos as THREE.Vector3 + const distance = fromPos.distanceTo(toPos) + const duration = Math.max(1.0, distance / speed) + + if (!this.travelAnimation) { + return new Promise(resolve => { + this.travelAnimation = gsap.fromTo(this.position, { + x: fromPos.x, + y: fromPos.y, + z: fromPos.z, + }, { + x: toPos.x, + y: toPos.y, + z: toPos.z, + duration, + ease: 'power2.inOut', + onComplete: () => { + this.travelAnimation = null + resolve() + this.onActionCompleted() + }, + onUpdate: () => { + + for (let i = 0; i < this.runningStepTaskList.length; i++) { + const task = this.runningStepTaskList[i] + if (task.isCompleted == false) { + if (this.position.distanceTo(task.position) < 0.1) { + task.isCompleted = true + this.runningStepTaskList.splice(0, i + 1) + const content: AmrMsg20020 = new AmrMsg20020(this.vehicleId) + content.CurLogicX = task.X + content.CurLogicY = task.Y + content.CurX = task.X + content.CurY = task.Y + content.CurOrientation = task.EndDirection * 90 + content.CurDirection = task.EndDirection + this.send20020(content) + break + } + } + } + } + }) + }) + } else { + this.travelAnimation.vars.x = toPos.x + this.travelAnimation.vars.y = toPos.y + this.travelAnimation.vars.z = toPos.z + const tt = this.travelAnimation.duration() + this.travelAnimation.duration(tt + duration) + this.travelAnimation.invalidate().restart(); + } + + } + + // 取货 + addLoad(height: number): void { + this.PickMode = 1 + this.OperationType = 4 + this.animationUpFork(height).then( + () => this.animationShowFork(1.4).then( + ()=>this.animationUpFork(height + 0.2).then( + ()=>this.animationHideFork().then( + ()=>this.animationDownFork().then(()=>{ + this.onActionCompleted() + }) + ) + ) + ) + ) + } + // 卸货 + addUnload(height: number): void { + this.PickMode = 2 + this.OperationType = 4 + this.animationUpFork(height + 0.2).then( + () => this.animationShowFork(1.4).then( + ()=>this.animationUpFork(height).then( + ()=>this.animationHideFork().then( + ()=>this.animationDownFork().then(()=>{ + this.onActionCompleted() + }) + ) + ) + ) + ) + } + + override animationShowFork(z: number): Promise{ + return null + }; + override animationHideFork(): Promise { + return null + } + override animationUpFork(y: number, time?: number = 3): Promise { + return null + } + override animationDownFork(): Promise { + return null + } + + //获取ptr的角度朝向 + getAmrOrientation(radY: number) { + radY = radY + Math.PI + while (radY < 0) { + radY += Math.PI * 2 + } + while (radY > Math.PI * 2) { + radY -= Math.PI * 2 + } + return THREE.MathUtils.radToDeg(radY) + } + +} diff --git a/src/modules/amr/ptr/cl2/Cl23dObject.ts b/src/modules/amr/ptr/cl2/Cl23dObject.ts index a4f6184..1ca81c4 100644 --- a/src/modules/amr/ptr/cl2/Cl23dObject.ts +++ b/src/modules/amr/ptr/cl2/Cl23dObject.ts @@ -24,124 +24,13 @@ import { type LogicDirection, TaskCompletedData, TaskModeChangeData, TaskStatusChangeData, TaskTypeChangeData, AmrMsg20010 } from "@/core/manager/amr/AmrMessageDefine"; import {worldModel} from "@/core/manager/WorldModel"; +import PtrObject from "../PtrObject"; +import Viewport from "@/core/engine/Viewport"; - -type CStepTaskType = "MOVE" | "MOVE_BACKWARD" | "ROTATION" | "LOAD" | "UNLOAD" | "CHARGE" - -interface StepTask { - SeqNo: number; - StepTaskType: CStepTaskType; - OperationType: 0 | 1 | 2 | 3 | 4 | 5 | 135 | 136; - PickMode: 0 | 1 | 2 | 3 | 4 | 5 | 6; - X: number; - Y: number; - Speed: number; - EndDirection: 0 | 1 | 2 | 3 | 15; - ChargeLocation: number; - GoodsSlotHeight: number; - position: THREE.Vector3; - isCompleted: boolean; -} - -export default class Cl23dObject extends THREE.Object3D { - - private item: ItemJson +export default class Cl23dObject extends PtrObject { private _cl2Entity: Cl2Entity = null - private currentStepTaskList: StepTask[] = [] - private runningStepTask: StepTask = null - private runningStepTaskList: StepTask[] = [] - private travelAnimation: core.Tween = null - - private currentLogicX: number = -1 - private currentLogicY: number = -1 - private currentDirection: LogicDirection = 15 - private sendMessageQueue: AmrMsg[] = [] - - public Battery: number = 100 - - private __TaskMode: CTaskMode = 0 - private __OperationType: COperationType = 0 - private __TaskStatus: CEventId = 0 - private __PickMode: CPickMode = 0 - - get TaskMode(): CTaskMode { - return this.__TaskMode - } - - set TaskMode(value: CTaskMode) { - if (this.__TaskMode != value) { - const info = new TaskModeChangeData(this.__TaskMode, value) - const msg = new AmrMsg20011(this.vehicleId, info) - msg.TaskMode = value - this.send20011(msg) - } - this.__TaskMode = value - } - - get OperationType(): COperationType { - return this.__OperationType - } - - set OperationType(value: COperationType) { - if (this.__OperationType != value) { - const info = new TaskTypeChangeData(this.__OperationType, value) - const msg = new AmrMsg20011(this.vehicleId, info) - msg.TaskMode = this.TaskMode - this.send20011(msg) - } - this.__OperationType = value - } - - get TaskStatus(): CEventId { - return this.__TaskStatus - } - - set TaskStatus(value: CEventId) { - if (this.__TaskStatus != value) { - - if (value != 1 && value != 4 && value != 8) { - const info = new TaskStatusChangeData(this.OperationType) - const msg = new AmrMsg20011(this.vehicleId, info) - msg.TaskMode = this.TaskMode - msg.EventId = value - this.send20011(msg) - } else if (value == 4) { - - const info = new TaskCompletedData(this.OperationType) - info.Battery = this.Battery - info.OperationResult = 0 - info.CurLogicX = this.currentLogicX - info.CurLogicY = this.currentLogicY - info.CurX = this.currentLogicX - info.CurY = this.currentLogicY - info.CurDirection = this.currentDirection - info.CurOrientation = THREE.MathUtils.radToDeg(this.rotation.y) - const msg = new AmrMsg20011(this.vehicleId, info) - msg.TaskMode = this.TaskMode - this.send20011(msg) - } - - } - } - - get PickMode(): CPickMode { - return this.__PickMode - } - set PickMode(value: CPickMode) { - if (this.__PickMode !== value) { - this.__PickMode = value - } - } - - private bootTime: number = 0 - - // 心跳间隔 UInt32 单位: s - private heartBeatInterval: number = 0 - // 小车所有上报消息重试间隔(未收到应答消息时重发消息) UInt32 单位: s - private mqRetryInterval: number = 3 - public get cl2Entity(): Cl2Entity { if (!this._cl2Entity) { const cl2: Cl2Entity = Model.getCl2(this.item.id) as Cl2Entity @@ -150,13 +39,9 @@ export default class Cl23dObject extends THREE.Object3D { return this._cl2Entity } - public vehicleId: number + constructor(item: ItemJson, viewport: Viewport, option?: RendererCudOption) { + super(item, viewport) - private clock = new THREE.Clock() - - constructor(item: ItemJson, option?: RendererCudOption) { - super() - this.item = item if (!Cl23DGraphics.ptrPedestalGeometry) { Cl23DGraphics.ptrPedestalGeometry = Cl23DGraphics.createPtrPedestal() } @@ -187,661 +72,13 @@ export default class Cl23dObject extends THREE.Object3D { groupPillar.add(ptrPillarMesh) groupPillar.add(ptrForkMesh) this.add(groupPillar) - - this.vehicleId = parseInt(this.cl2Entity.id) - this.cl2Entity.viewport.addFrameTimerCallback(this.cl2Entity.id, this.onFrameTimer.bind(this)) - if (!worldModel.state.runState.isVirtual) { - this.subscribeMessage('/wcs_server/' + this.cl2Entity.id) - this.subscribeMessage('/agv_robot/status') - } - } - - private AGVModel = "CYBER-LIFT-A_V1.0" - private AGVFnModel = "FITBOTS-CYBER-LIFT-1000_V1.0" - - - private heartBeatTimeCount: number = 0 - private mqRetryTimeCount: number = 0 - - private onFrameTimer() { - if (!worldModel.state.runState.isVirtual) { - return - } - const delta = this.clock.getDelta() - this.mqRetryTimeCount += delta - if (this.mqRetryInterval > 0 && this.mqRetryTimeCount >= this.mqRetryInterval) { - this.mqRetryTimeCount = 0 - // 在此处理消息重试 - if (this.sendMessageQueue.length > 0) { - this.sendMessage(this.sendMessageQueue[0]) - } - } - this.heartBeatTimeCount += delta - if (this.heartBeatInterval > 0 && this.heartBeatTimeCount >= this.heartBeatInterval) { - this.heartBeatTimeCount = 0 - // 在此处发送心跳报文 - this.sendHeartBeat() - } - } - - - // 开机 - boot() { - this.bootTime = Date.now(); - this.computeLogicXYAndDirection(); - - if (!worldModel.state.runState.isVirtual) { - return - } - this.subscribeMessage('/wcs_server/' + this.cl2Entity.id) - this.send20147() - setTimeout(() => { - this.send20149() - this.TaskMode = 1 - // 检查当前所在位置和方向 根据车当前所在的xz坐标获取地标 - setTimeout(() => { - this.sendCurrentPositionAndDirection() - setTimeout(() => { - this.send20150() - }, 1000) - }, 1000) - }, 2000) - - } - - // 关机 - shutdown() { - if (!worldModel.state.runState.isVirtual) { - return - } - const content = new AmrMsg20148(this.vehicleId) - // 电量 - content.Battery = 100 - content.CreateMonoTime = Date.now() - this.bootTime - content.Uptime = content.CreateMonoTime - const m20148 = new AmrMsg(content) - this.sendMessage(m20148) - } - - // 开机上报 - send20147() { - const content = new AmrMsg20147(this.vehicleId) - content.AGVModel = this.AGVModel - content.AGVFnModel = this.AGVFnModel - // 电量 - content.Battery = 100 - content.CreateMonoTime = Date.now() - this.bootTime - const m20147 = new AmrMsg(content) - this.sendMessage(m20147) - } - - // 主程序启动上报 - send20149() { - const content = new AmrMsg20149(this.vehicleId) - content.AGVModel = this.AGVModel - content.AGVFnModel = this.AGVFnModel - // 电量 - content.Battery = 100 - content.CreateMonoTime = Date.now() - this.bootTime - const m20149 = new AmrMsg(content) - this.sendMessage(m20149) - } - - // 上线上报 - send20150() { - const content = new AmrMsg20150(this.vehicleId) - content.AGVModel = this.AGVModel - content.AGVFnModel = this.AGVFnModel - content.Battery = 100 - content.CreateMonoTime = Date.now() - this.bootTime - const m20150 = new AmrMsg(content) - this.sendMessage(m20150) - } - - // 上报当前位姿,地标和方向 - sendCurrentPositionAndDirection() { - if (this.currentLogicX <= 0 || this.currentLogicY <= 0) { - // 当前车辆所在位置未找到 - const content = new AmrMsg20250(this.vehicleId) - content.Duration = 0 - content.ErrCode = 5 - content.ErrCodeName = AmrErrorCode[5].ErrCodeName - content.ErrEvtType = 1 - content.ErrLevel = 14 - content.ErrLifecycle = 2 - content.CreateMonoTime = Date.now() - this.bootTime - const m20250 = new AmrMsg(content) - this.sendMessage(m20250) - } else { - // 发送正常地标信息 - const content = new AmrMsg20020(this.vehicleId) - content.CurDirection = this.currentDirection - content.CurLogicX = this.currentLogicX - content.CurLogicY = this.currentLogicY - content.CurX = this.currentLogicX - content.CurY = this.currentLogicY - content.CreateMonoTime = Date.now() - this.bootTime - const m20020 = new AmrMsg(content) - this.sendMessage(m20020) - } - } - - send20010() { - const content: AmrMsg20010 = new AmrMsg20010(this.vehicleId) - content.CurX = this.currentLogicX - content.CurY = this.currentLogicY - content.CurDirection = this.currentDirection - content.OperationType = this.OperationType - content.Battery = this.Battery - content.OperationResult = 0 - content.Summary = { - ActuatorsData: [ - { - MechNo: 1, - Name: "Mech1", - PickMode: this.PickMode - } - ] - } - - - const m20010 = new AmrMsg(content) - this.sendMessage(m20010) - } - - send20011(content: AmrMsg20011) { - const m20011 = new AmrMsg>(content) - this.sendMessage(m20011) - } - - send20020(content: AmrMsg20020) { - const m20020 = new AmrMsg(content) - this.sendMessage(m20020) - } - - send20060() { - const content = new AmrMsg20060(this.vehicleId) - content.CreateMonoTime = Date.now() - this.bootTime - content.CurBattery = new CurBatteryData() - content.CurLogicX = this.currentLogicX - content.CurLogicY = this.currentLogicY - content.CurOrientation = THREE.MathUtils.radToDeg(this.rotation.y); - content.CurX = this.position.x; - content.CurY = this.position.z; - content.X = this.position.x; - content.Y = this.position.z; - const m20060 = new AmrMsg(content) - this.sendMessage(m20060) - } - - send20250(content: AmrMsg20250) { - this.sendMessage(new AmrMsg(content)) - } - - subscribeMessage(topic: string) { - worldModel.envManager.client.subscribe(topic, {qos: 0}) - } - - sendMessage(msg: AmrMsg) { - if (!worldModel.state.runState.isVirtual) { - return - } - console.log('send message:', JSON.stringify(msg)) - if (this.sendMessageQueue.indexOf(msg) < 0) { - this.sendMessageQueue.push(msg) - } - if (this.sendMessageQueue.length <= 0) { - this.mqRetryTimeCount = 0 - } - worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(msg)) - this.heartBeatTimeCount = 0 - } - - sendHeartBeat() { - if (!worldModel.state.runState.isVirtual) { - return - } - const content = new AmrMsg20100(this.vehicleId) - content.Temperature = {Battery: this.Battery} - const m20100 = new AmrMsg(content) - worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(m20100)) - } - - sendAck(seqNo: number, vehicleId: number) { - if (!worldModel.state.runState.isVirtual) { - return - } - const msg20050 = new AmrMsg20050(seqNo, vehicleId) - const ack = new AmrMsg(msg20050) - this.heartBeatTimeCount = 0 - worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(ack)) - } - - /*==========RCS消息处理============*/ - // 处理任务 - handle10010Message(data: AmrMsg10010) { - if (this.currentStepTaskList.length > 0) { - if (this.runningStepTask.OperationType == 0 && this.runningStepTask.X == data.StartX && this.runningStepTask.Y == data.StartY) { - // this.currentStepTaskList = [] - this.makeStepTask(data) - this.executeTask() - } else { - // 此处应该有错误处理 - } - } else { - this.makeStepTask(data) - this.executeTask() - } - } - - handle10050Message(data: AmrMsg) { - if (this.sendMessageQueue.length > 0 && data.content.SeqNo === this.sendMessageQueue[0].content.SeqNo) { - this.mqRetryTimeCount = 0 - this.sendMessageQueue.shift() - } - } - - handle10060Message(data: AmrMsg) { - this.mqRetryInterval = data.content.MqRetryTime - this.heartBeatInterval = data.content.HeartBeat - this.TaskMode = 0 - } - - // 处理状态查询 - handle10110Message(data: AmrMsg) { - this.send20060() - } - - // 取消任务 - handle10120Message(data: AmrMsg) { - - } - - /*==========真车消息处理============*/ - - // 计算逻辑方向 - computeLogicXYAndDirection() { - let ra = this.rotation.y - while (ra > Math.PI * 2) { - ra -= Math.PI * 2 - } - while (ra < 0) { - ra += Math.PI * 2 - } - const ddra = Math.PI / 8 - if (ra >= ddra * 7 || ra < ddra) { - this.currentDirection = 0; - - } else if (ra >= ddra && ra < ddra * 3) { - this.currentDirection = 3; - - } else if (ra >= ddra * 3 && ra < ddra * 5) { - this.currentDirection = 2; - - } else if (ra >= ddra * 5 && ra < ddra * 7) { - this.currentDirection = 1; - } else { - this.currentDirection = 15; - } - - const pointItem = Model.getItemByXYZ(this.position.x, this.position.y, this.position.z) - if (!pointItem || !pointItem.logicX || !pointItem.logicY) { - this.currentLogicX = -1; - this.currentLogicY = -1; - } else { - this.currentLogicX = pointItem.logicX; - this.currentLogicY = pointItem.logicY; - } - - } - - makeStepTask(data: AmrMsg10010) { - - let currentStepTask: StepTask = this.runningStepTask - if (currentStepTask == null) { - - currentStepTask = { - SeqNo: 0, - StepTaskType: "MOVE", - OperationType: 0, - PickMode: 0, - X: this.currentLogicX, - Y: this.currentLogicY, - Speed: 1000, - EndDirection: this.currentDirection, - ChargeLocation: 0, - GoodsSlotHeight: 0, - position: this.position, - isCompleted: true - } - } - let endDirection = currentStepTask.EndDirection - - if (data.Link.length > 0) { - for (let i = 0; i < data.Link.length; i++) { - const link = data.Link[i] - if ((currentStepTask.X == link.X && currentStepTask.Y == link.Y) - || (currentStepTask.X != link.X && currentStepTask.Y != link.Y)) { - continue - } else if (currentStepTask.X < link.X) { - if (link.Speed > 0) { - endDirection = 0 - } else { - endDirection = 2 - } - } else if (currentStepTask.X > link.X) { - if (link.Speed > 0) { - endDirection = 2 - } else { - endDirection = 0 - } - } else if (currentStepTask.Y < link.Y) { - if (link.Speed > 0) { - endDirection = 1 - } else { - endDirection = 3 - } - } else if (currentStepTask.Y > link.Y) { - if (link.Speed > 0) { - endDirection = 3 - } else { - endDirection = 1 - } - } - - if (endDirection != currentStepTask.EndDirection) { - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: "ROTATION", - OperationType: 0, - PickMode: 0, - X: link.X, - Y: link.Y, - Speed: link.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(link.X, link.Y) as THREE.Vector3, - isCompleted: false - } - currentStepTask = stepTask - this.currentStepTaskList.push(stepTask) - } - - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: link.Speed > 0 ? "MOVE" : "MOVE_BACKWARD", - OperationType: 0, - PickMode: 0, - X: link.X, - Y: link.Y, - Speed: link.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(link.X, link.Y) as THREE.Vector3, - isCompleted: false - } - currentStepTask = stepTask - this.currentStepTaskList.push(stepTask) - } - } - - - if (data.OperationType == 0 && data.EndDirection >= 0 && data.EndDirection <= 3) { - endDirection = data.EndDirection; - } else if (data.OperationType == 3 && data.ChargeDirection >= 0 && data.ChargeDirection <= 3) { - endDirection = data.ChargeDirection - } else if (data.OperationType == 4 && data.GoodsSlotDirection >= 0 && data.GoodsSlotDirection <= 3) { - if (data.GoodsSlotDirection == 0) { - endDirection = 3 - } else { - endDirection = (data.GoodsSlotDirection - 1) as LogicDirection - } - } - if (endDirection != currentStepTask.EndDirection) { - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: "ROTATION", - OperationType: 0, - PickMode: 0, - X: data.EndX, - Y: data.EndY, - Speed: currentStepTask.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, - isCompleted: false - } - this.currentStepTaskList.push(stepTask) - } - if (data.OperationType == 3) { - - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: "CHARGE", - OperationType: 3, - PickMode: 0, - X: data.EndX, - Y: data.EndY, - Speed: currentStepTask.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, - isCompleted: false - } - this.currentStepTaskList.push(stepTask) - - } else if (data.OperationType == 4) { - - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: data.PickMode == 1 ? "LOAD" : "UNLOAD", - OperationType: 4, - PickMode: data.PickMode, - X: data.EndX, - Y: data.EndY, - Speed: currentStepTask.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, - isCompleted: false - } - this.currentStepTaskList.push(stepTask) - - } else { - - } - - } - - executeTask() { - this.TaskMode = 2 - while (this.currentStepTaskList.length > 0) { - const stepTask = this.currentStepTaskList[0] - if (this.runningStepTask) { - if ((stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD") - && stepTask.EndDirection == this.runningStepTask.EndDirection - && (stepTask.Speed > 0) == (this.runningStepTask.Speed > 0)) { - this.runningStepTask = stepTask - this.currentStepTaskList.shift() - this.runningStepTaskList.push(stepTask) - - this.addTravel(stepTask.X, stepTask.Y, stepTask.Speed/1000) - - } else { - break - } - } else { - this.runningStepTask = stepTask - this.currentStepTaskList.shift() - this.runningStepTaskList.push(stepTask) - if (stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD") { - this.addTravel(stepTask.X, stepTask.Y, stepTask.Speed/1000) - } else if (stepTask.StepTaskType == "ROTATION") { - this.addRotation(stepTask.EndDirection) - } else if (stepTask.StepTaskType == "LOAD") { - this.addLoad(stepTask.GoodsSlotHeight/1000) - } else if (stepTask.StepTaskType == "UNLOAD") { - this.addUnload(stepTask.GoodsSlotHeight/1000) - } - } - } - } - - onActionCompleted() { - this.runningStepTaskList = [] - this.runningStepTask = null - this.computeLogicXYAndDirection() - // 当前所有动作执行完毕 - if (this.currentStepTaskList.length <= 0) { - this.send20010() - } - this.PickMode = 0 - this.OperationType = 0 - this.TaskMode = 0 - this.executeTask() } /*==========动画处理============*/ - // 转 - addRotation(direction: number): Promise { - let rad = 0 - switch (direction) { - case 1: - rad = Math.PI / 2 * 3 - break - case 2: - rad = Math.PI - break - case 3: - rad = Math.PI / 2 - break - default: - rad = 0 - } - const quat1 = new THREE.Quaternion().setFromEuler(this.rotation) - const euler: Euler = new Euler(this.rotation.x, rad, this.rotation.z) - const quat2 = new THREE.Quaternion().setFromEuler(euler) - const angleDiff = quat1.angleTo(quat2) - console.log(rad, this.rotation.y, angleDiff) - const tr = this.rotation.y + angleDiff - let time = Math.abs(angleDiff) / (Math.PI / 7) - const duration = time - - return new Promise(resolve => { - gsap.to(this.rotation, { - y: tr, - duration, - ease: 'none', - onComplete: ()=>{ - resolve() - this.onActionCompleted() - } - }) - }) - } - - // 走 - addTravel(logicX: number, logicY: number, speed: number = 1): Promise { - this.OperationType = 0 - this.PickMode = 0 - const pos = Model.getPositionByLogicXY(logicX, logicY) - - const fromPos = this.position - const toPos = pos as THREE.Vector3 - const distance = fromPos.distanceTo(toPos) - const duration = Math.max(1.0, distance / speed) - - if (!this.travelAnimation) { - return new Promise(resolve => { - this.travelAnimation = gsap.fromTo(this.position, { - x: fromPos.x, - y: fromPos.y, - z: fromPos.z, - }, { - x: toPos.x, - y: toPos.y, - z: toPos.z, - duration, - ease: 'power2.inOut', - onComplete: () => { - this.travelAnimation = null - resolve() - this.onActionCompleted() - }, - onUpdate: () => { - for (let i = 0; i < this.runningStepTaskList.length; i++) { - const task = this.runningStepTaskList[i] - if (task.isCompleted == false) { - if (this.position.distanceTo(task.position) < 0.1) { - task.isCompleted = true - this.runningStepTaskList.splice(0, i + 1) - const content: AmrMsg20020 = new AmrMsg20020(this.vehicleId) - content.CurLogicX = task.X - content.CurLogicY = task.Y - content.CurX = task.X - content.CurY = task.Y - // content.CurOrientation = task.Orientation - content.CurDirection = task.EndDirection - this.send20020(content) - break - } - } - } - } - }) - }) - } else { - this.travelAnimation.vars.x = toPos.x - this.travelAnimation.vars.y = toPos.y - this.travelAnimation.vars.z = toPos.z - const tt = this.travelAnimation.duration() - this.travelAnimation.duration(tt + duration) - this.travelAnimation.invalidate().restart(); - } - - } - - // 取货 - addLoad(height: number): void { - this.PickMode = 1 - this.OperationType = 4 - this.animationUpFork(height).then( - () => this.animationShowFork(1.4).then( - ()=>this.animationUpFork(height + 0.2).then( - ()=>this.animationHideFork().then( - ()=>this.animationDownFork().then(()=>{ - this.onActionCompleted() - }) - ) - ) - ) - ) - } - // 卸货 - addUnload(height: number): void { - this.PickMode = 2 - this.OperationType = 4 - this.animationUpFork(height + 0.2).then( - () => this.animationShowFork(1.4).then( - ()=>this.animationUpFork(height).then( - ()=>this.animationHideFork().then( - ()=>this.animationDownFork().then(()=>{ - this.onActionCompleted() - }) - ) - ) - ) - ) - } - animationShowFork(z: number): Promise { + override animationShowFork(z: number): Promise { const ptrPillar = this.getObjectByName('ptrPillar') const time = 3 @@ -857,11 +94,11 @@ export default class Cl23dObject extends THREE.Object3D { } - animationHideFork(): Promise { + override animationHideFork(): Promise { return this.animationShowFork(0) } - animationUpFork(y: number, time?: number = 3): Promise { + override animationUpFork(y: number, time?: number = 3): Promise { const ptrFork = this.getObjectByName('ptrFork') const ptrPillar = this.getObjectByName('ptrPillar') const pz = ptrPillar.position.z @@ -895,7 +132,7 @@ export default class Cl23dObject extends THREE.Object3D { }) } - animationDownFork(): Promise { + override animationDownFork(): Promise { return this.animationUpFork(0) } } diff --git a/src/modules/amr/ptr/cl2/Cl2Renderer.ts b/src/modules/amr/ptr/cl2/Cl2Renderer.ts index 333ba87..bc8daf5 100644 --- a/src/modules/amr/ptr/cl2/Cl2Renderer.ts +++ b/src/modules/amr/ptr/cl2/Cl2Renderer.ts @@ -55,7 +55,7 @@ export default class PtrRenderer extends BaseRenderer { return null } - const group = new Cl23dObject(item, option) + const group = new Cl23dObject(item, this.tempViewport, option) group.name = PtrRenderer.POINT_NAME // 设置位置 diff --git a/src/modules/amr/ptr/clx/Clx3DGraphics.ts b/src/modules/amr/ptr/clx/Clx3DGraphics.ts new file mode 100644 index 0000000..e4190e1 --- /dev/null +++ b/src/modules/amr/ptr/clx/Clx3DGraphics.ts @@ -0,0 +1,400 @@ +import * as THREE from "three"; +import {CSG} from "three-csg-ts"; +//@ts-ignore +import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js' + +export default class Clx3DGraphics { + + // 创建clx的底座 + static createClxPedestal(): THREE.BufferGeometry { + + const width = 1.65 + const depth = 1.65 + const dd = 0.05 + const bd = 0.175 + + const shape = new THREE.Shape() + shape.moveTo(-width / 2, -depth / 2 + bd) + shape.lineTo(-width / 2 + bd, -depth / 2) + shape.lineTo(width / 2 - bd, -depth / 2) + shape.lineTo(width / 2, -depth / 2 + bd) + + shape.lineTo(width / 2, -0.285) + shape.lineTo(-0.475, -0.285) + shape.lineTo(-0.475, -0.125) + shape.lineTo(width / 2, -0.125) + shape.lineTo(width / 2, 0.125) + shape.lineTo(-0.475, 0.125) + shape.lineTo(-0.475, 0.285) + shape.lineTo(width / 2, 0.285) + + shape.lineTo(width / 2, depth / 2 - bd) + shape.lineTo(width / 2 - bd, depth / 2) + shape.lineTo(-width / 2 + bd, depth / 2) + shape.lineTo(-width / 2, depth / 2 - bd) + shape.closePath() // 闭合路径 + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.22, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) + const mesh = new THREE.Mesh(geometry, material) + + mesh.updateMatrix() + + // 倒角 + const shapeD = new THREE.Shape() + shapeD.moveTo(-0.02, -0.02) + shapeD.lineTo(0.02, 0.02) + shapeD.lineTo(-0.02, 0.02) + shapeD.closePath() + + const shapeDW = new THREE.Shape() + shapeDW.moveTo(0.02, -0.02) + shapeDW.lineTo(0.02, 0.02) + shapeDW.lineTo(-0.02, 0.02) + shapeDW.closePath() + + const shapeDW1 = new THREE.Shape() + shapeDW1.moveTo(-0.02, -0.02) + shapeDW1.lineTo(0.02, -0.02) + shapeDW1.lineTo(-0.02, 0.02) + shapeDW1.closePath() + + + const positionDs: THREE.Vector3[] = [ + new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd), + new THREE.Vector3(-width / 2 + bd, 0.20, -depth / 2), + new THREE.Vector3(width / 2 - bd, 0.20, -depth / 2), + new THREE.Vector3(width / 2, 0.20, -depth / 2 + bd), + new THREE.Vector3(width / 2, 0.20, depth / 2 - bd), + new THREE.Vector3(width / 2 - bd, 0.20, depth / 2), + new THREE.Vector3(-width / 2 + bd, 0.20, depth / 2), + new THREE.Vector3(-width / 2, 0.20, depth / 2 - bd), + new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd) + ] + + let result: THREE.Mesh = mesh + + for (let i = 0; i < positionDs.length - 1; i++) { + + const curveD = new THREE.CatmullRomCurve3([positionDs[i], positionDs[i + 1]], + false, + 'catmullrom', + 0) + const optionsD = { + steps: 1, + bevelEnabled: false, + extrudePath: curveD + } + let geometryD: THREE.BufferGeometry = null + if (i == 1) { + geometryD = new THREE.ExtrudeGeometry(shapeDW, optionsD) + } else if (i == 5) { + geometryD = new THREE.ExtrudeGeometry(shapeDW1, optionsD) + } else { + geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) + } + const meshD = new THREE.Mesh(geometryD, material) + meshD.updateMatrix() + result = CSG.subtract(result, meshD) + } + return result.geometry + } + + // 创建clx的立柱 + static createClxPillar(): THREE.BufferGeometry { + // 606.5 + const width = 0.3 + const depth = 1.188 + const dd = 0.05 + const shape = new THREE.Shape() + shape.moveTo(-0.744, -0.594 + dd) + shape.lineTo(-0.744 + dd, -0.594) + shape.lineTo(-0.744 + width - dd, -0.594) + shape.lineTo(-0.744 + width, -0.594 + dd) + + shape.lineTo(-0.744 + width, -0.4) + shape.lineTo(-0.744 + width - 0.08, -0.4) + shape.lineTo(-0.744 + width - 0.08, 0.4) + shape.lineTo(-0.744 + width, 0.4) + + shape.lineTo(-0.744 + width, -0.594 + depth - dd) + shape.lineTo(-0.744 + width - dd, -0.594 + depth) + shape.lineTo(-0.744 + dd, -0.594 + depth) + shape.lineTo(-0.744, -0.594 + depth - dd) + // shape.lineTo(-0.728, -0.594 + dd); + // shape.closePath() + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.22, 0), new THREE.Vector3(0, 3.357, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + // const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}); + // const mesh = new THREE.Mesh(geometry, material); + return new THREE.ExtrudeGeometry(shape, options) + + } + + // 创建clx的叉 + static createClxFork(): THREE.BufferGeometry { + // 606.5 + const width = 0.3 + const depth = 1.188 + const dd = 0.05 + const fdx = 0.06 + const fdy = 0.03 + const shape = new THREE.Shape() + + shape.moveTo(-0.744 + width - 0.08, -0.4) + shape.lineTo(-0.744 + width + 0.02, -0.4) + shape.lineTo(-0.744 + width + 0.02, -0.275) + shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.275) + shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.275 + fdy) + shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.135 - fdy) + shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.135) + shape.lineTo(-0.744 + width + 0.02, -0.135) + shape.lineTo(-0.744 + width + 0.02, 0.135) + shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.135) + shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.135 + fdy) + shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.275 - fdy) + shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.275) + shape.lineTo(-0.744 + width + 0.02, 0.275) + shape.lineTo(-0.744 + width + 0.02, 0.4) + shape.lineTo(-0.744 + width - 0.08, 0.4) + + shape.closePath() + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 1.287, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) + const mesh = new THREE.Mesh(geometry, material) + mesh.updateMatrix() + + const shapeD = new THREE.Shape() + + + shapeD.moveTo(-0.744 + width + 0.02, -0.3) + shapeD.lineTo(-0.744 + width + 0.02 + 1.3, -0.3) + shapeD.lineTo(-0.744 + width + 0.02 + 1.3, 0.3) + shapeD.lineTo(-0.744 + width + 0.02, 0.3) + shape.closePath() + + // 拉伸轨迹线 + const curveD = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.07, 0), new THREE.Vector3(0, 1.3, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsD = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curveD // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) + const meshD = new THREE.Mesh(geometryD, material) + + meshD.updateMatrix() + + // 布尔运算 + const result = CSG.subtract(mesh, meshD) + + return result.geometry + } + + // 创建clx的铰链 + static createClxGemel(isLeft: boolean = false) { + const width = 0.08 + const depth = 0.9 + const dd = 0.02 + const shape = new THREE.Shape() + shape.moveTo(-width / 2, -depth / 2 + dd) + shape.lineTo(-width / 2 + dd, -depth / 2) + shape.lineTo(width / 2 - dd, -depth / 2) + shape.lineTo(width / 2, -depth / 2 + dd) + shape.lineTo(width / 2, depth / 2 - dd) + shape.lineTo(width / 2 - dd, depth / 2) + shape.lineTo(-width / 2 + dd, depth / 2) + shape.lineTo(-width / 2, depth / 2 - dd) + shape.closePath() + + const shapeBar = new THREE.Shape() + shapeBar.moveTo(0, 0) // 起点在圆心 + shapeBar.absarc(0, 0, 0.02, 0, Math.PI * 2, false) // 从0到π绘制半圆 + shapeBar.closePath() + + const geometries: THREE.BufferGeometry[] = [] + + const curveL1 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.35 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.30 + (isLeft ? 0.05 : 0), 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveL2 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.15 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.1 + (isLeft ? 0.05 : 0), 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveL3 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0.15 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.1 - (isLeft ? 0.05 : 0), 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveL4 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0.30 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.35 - (isLeft ? 0.05 : 0), 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + if (!isLeft) { + const curveBar1 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.36, -0.4, 0), new THREE.Vector3(0.36, -0.4, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveBar2 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.36, 0, 0), new THREE.Vector3(0.36, 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveBar3 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.36, 0.4, 0), new THREE.Vector3(0.36, 0.4, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + geometries.push(new THREE.ExtrudeGeometry(shapeBar, { + steps: 1, + bevelEnabled: false, + extrudePath: curveBar1 + })) + geometries.push(new THREE.ExtrudeGeometry(shapeBar, { + steps: 1, + bevelEnabled: false, + extrudePath: curveBar2 + })) + geometries.push(new THREE.ExtrudeGeometry(shapeBar, { + steps: 1, + bevelEnabled: false, + extrudePath: curveBar3 + })) + } + + const geometryL1 = new THREE.ExtrudeGeometry(shape, { + steps: 1, + bevelEnabled: false, + extrudePath: curveL1 + }) + + const geometryL2 = new THREE.ExtrudeGeometry(shape, { + steps: 1, + bevelEnabled: false, + extrudePath: curveL2 + }) + + const geometryL3 = new THREE.ExtrudeGeometry(shape, { + steps: 1, + bevelEnabled: false, + extrudePath: curveL3 + }) + + const geometryL4 = new THREE.ExtrudeGeometry(shape, { + steps: 1, + bevelEnabled: false, + extrudePath: curveL4 + }) + + geometries.push(geometryL1, geometryL2, geometryL3, geometryL4) + + return mergeGeometries(geometries) + } + + // 创建clx叉的背板 + static createClxForkBasePlate(): THREE.BufferGeometry { + // 606.5 + const width = 0.3 + const depth = 1.188 + const dd = 0.05 + const fdx = 0.06 + const fdy = 0.03 + + const shape = new THREE.Shape(); + shape.moveTo(-0.744 + 0.1, -0.5); + shape.lineTo(-0.744 + 0.1, 0.5); + shape.lineTo(-0.744 + 0.2, 0.5); + shape.lineTo(-0.744 + 0.2, -0.5); + shape.closePath() + + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 1.287, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const options = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curve // 设置挤出轨迹 + } + return new THREE.ExtrudeGeometry(shape, options) + } + + static clxPedestalGeometry: THREE.BufferGeometry = null + static clxPillarGeometry: THREE.BufferGeometry = null + static clxForkGeometry: THREE.BufferGeometry = null + static clxGemelGeometryL: THREE.BufferGeometry = null + static clxGemelGeometryR: THREE.BufferGeometry = null + static clxForkBasePlateGeometry: THREE.BufferGeometry = null +} diff --git a/src/modules/amr/ptr/clx/Clx3dObject.ts b/src/modules/amr/ptr/clx/Clx3dObject.ts index 222ef39..f1be300 100644 --- a/src/modules/amr/ptr/clx/Clx3dObject.ts +++ b/src/modules/amr/ptr/clx/Clx3dObject.ts @@ -3,437 +3,47 @@ import {CSG} from 'three-csg-ts' import gsap from 'gsap' //@ts-ignore import {mergeGeometries} from 'three/addons/utils/BufferGeometryUtils.js' +import Clx3DGraphics from "@/modules/amr/ptr/clx/Clx3DGraphics"; +import Viewport from "@/core/engine/Viewport"; +import PtrObject from "@/modules/amr/ptr/PtrObject"; -export default class Clx3dObject extends THREE.Object3D { - - // 创建clx的底座 - private static createClxPedestal(): THREE.BufferGeometry { - - const width = 1.65 - const depth = 1.65 - const dd = 0.05 - const bd = 0.175 - - const shape = new THREE.Shape() - shape.moveTo(-width / 2, -depth / 2 + bd) - shape.lineTo(-width / 2 + bd, -depth / 2) - shape.lineTo(width / 2 - bd, -depth / 2) - shape.lineTo(width / 2, -depth / 2 + bd) - - shape.lineTo(width / 2, -0.285) - shape.lineTo(-0.475, -0.285) - shape.lineTo(-0.475, -0.125) - shape.lineTo(width / 2, -0.125) - shape.lineTo(width / 2, 0.125) - shape.lineTo(-0.475, 0.125) - shape.lineTo(-0.475, 0.285) - shape.lineTo(width / 2, 0.285) - - shape.lineTo(width / 2, depth / 2 - bd) - shape.lineTo(width / 2 - bd, depth / 2) - shape.lineTo(-width / 2 + bd, depth / 2) - shape.lineTo(-width / 2, depth / 2 - bd) - shape.closePath() // 闭合路径 - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.22, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelEnabled: false, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometry = new THREE.ExtrudeGeometry(shape, options) - const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) - const mesh = new THREE.Mesh(geometry, material) - - mesh.updateMatrix() - - // 倒角 - const shapeD = new THREE.Shape() - shapeD.moveTo(-0.02, -0.02) - shapeD.lineTo(0.02, 0.02) - shapeD.lineTo(-0.02, 0.02) - shapeD.closePath() - - const shapeDW = new THREE.Shape() - shapeDW.moveTo(0.02, -0.02) - shapeDW.lineTo(0.02, 0.02) - shapeDW.lineTo(-0.02, 0.02) - shapeDW.closePath() - - const shapeDW1 = new THREE.Shape() - shapeDW1.moveTo(-0.02, -0.02) - shapeDW1.lineTo(0.02, -0.02) - shapeDW1.lineTo(-0.02, 0.02) - shapeDW1.closePath() - - - const positionDs: THREE.Vector3[] = [ - new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd), - new THREE.Vector3(-width / 2 + bd, 0.20, -depth / 2), - new THREE.Vector3(width / 2 - bd, 0.20, -depth / 2), - new THREE.Vector3(width / 2, 0.20, -depth / 2 + bd), - new THREE.Vector3(width / 2, 0.20, depth / 2 - bd), - new THREE.Vector3(width / 2 - bd, 0.20, depth / 2), - new THREE.Vector3(-width / 2 + bd, 0.20, depth / 2), - new THREE.Vector3(-width / 2, 0.20, depth / 2 - bd), - new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd) - ] - - let result: THREE.Mesh = mesh - - for (let i = 0; i < positionDs.length - 1; i++) { - - const curveD = new THREE.CatmullRomCurve3([positionDs[i], positionDs[i + 1]], - false, - 'catmullrom', - 0) - const optionsD = { - steps: 1, - bevelEnabled: false, - extrudePath: curveD - } - let geometryD: THREE.BufferGeometry = null - if (i == 1) { - geometryD = new THREE.ExtrudeGeometry(shapeDW, optionsD) - } else if (i == 5) { - geometryD = new THREE.ExtrudeGeometry(shapeDW1, optionsD) - } else { - geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) - } - const meshD = new THREE.Mesh(geometryD, material) - meshD.updateMatrix() - result = CSG.subtract(result, meshD) - } - return result.geometry - } - - // 创建clx的立柱 - private static createClxPillar(): THREE.BufferGeometry { - // 606.5 - const width = 0.3 - const depth = 1.188 - const dd = 0.05 - const shape = new THREE.Shape() - shape.moveTo(-0.744, -0.594 + dd) - shape.lineTo(-0.744 + dd, -0.594) - shape.lineTo(-0.744 + width - dd, -0.594) - shape.lineTo(-0.744 + width, -0.594 + dd) - - shape.lineTo(-0.744 + width, -0.4) - shape.lineTo(-0.744 + width - 0.08, -0.4) - shape.lineTo(-0.744 + width - 0.08, 0.4) - shape.lineTo(-0.744 + width, 0.4) - - shape.lineTo(-0.744 + width, -0.594 + depth - dd) - shape.lineTo(-0.744 + width - dd, -0.594 + depth) - shape.lineTo(-0.744 + dd, -0.594 + depth) - shape.lineTo(-0.744, -0.594 + depth - dd) - // shape.lineTo(-0.728, -0.594 + dd); - // shape.closePath() - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.22, 0), new THREE.Vector3(0, 3.357, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - // const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}); - // const mesh = new THREE.Mesh(geometry, material); - return new THREE.ExtrudeGeometry(shape, options) - - } - - // 创建clx的叉 - private static createClxFork(): THREE.BufferGeometry { - // 606.5 - const width = 0.3 - const depth = 1.188 - const dd = 0.05 - const fdx = 0.06 - const fdy = 0.03 - const shape = new THREE.Shape() - - shape.moveTo(-0.744 + width - 0.08, -0.4) - shape.lineTo(-0.744 + width + 0.02, -0.4) - shape.lineTo(-0.744 + width + 0.02, -0.275) - shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.275) - shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.275 + fdy) - shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.135 - fdy) - shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.135) - shape.lineTo(-0.744 + width + 0.02, -0.135) - shape.lineTo(-0.744 + width + 0.02, 0.135) - shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.135) - shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.135 + fdy) - shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.275 - fdy) - shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.275) - shape.lineTo(-0.744 + width + 0.02, 0.275) - shape.lineTo(-0.744 + width + 0.02, 0.4) - shape.lineTo(-0.744 + width - 0.08, 0.4) - - shape.closePath() - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 1.287, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometry = new THREE.ExtrudeGeometry(shape, options) - const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) - const mesh = new THREE.Mesh(geometry, material) - mesh.updateMatrix() - - const shapeD = new THREE.Shape() - - - shapeD.moveTo(-0.744 + width + 0.02, -0.3) - shapeD.lineTo(-0.744 + width + 0.02 + 1.3, -0.3) - shapeD.lineTo(-0.744 + width + 0.02 + 1.3, 0.3) - shapeD.lineTo(-0.744 + width + 0.02, 0.3) - shape.closePath() - - // 拉伸轨迹线 - const curveD = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.07, 0), new THREE.Vector3(0, 1.3, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const optionsD = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curveD // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) - const meshD = new THREE.Mesh(geometryD, material) - - meshD.updateMatrix() - - // 布尔运算 - const result = CSG.subtract(mesh, meshD) - - return result.geometry - } - - // 创建clx的铰链 - private static createClxGemel(isLeft: boolean = false) { - const width = 0.08 - const depth = 0.9 - const dd = 0.02 - const shape = new THREE.Shape() - shape.moveTo(-width / 2, -depth / 2 + dd) - shape.lineTo(-width / 2 + dd, -depth / 2) - shape.lineTo(width / 2 - dd, -depth / 2) - shape.lineTo(width / 2, -depth / 2 + dd) - shape.lineTo(width / 2, depth / 2 - dd) - shape.lineTo(width / 2 - dd, depth / 2) - shape.lineTo(-width / 2 + dd, depth / 2) - shape.lineTo(-width / 2, depth / 2 - dd) - shape.closePath() - - const shapeBar = new THREE.Shape() - shapeBar.moveTo(0, 0) // 起点在圆心 - shapeBar.absarc(0, 0, 0.02, 0, Math.PI * 2, false) // 从0到π绘制半圆 - shapeBar.closePath() - - const geometries: THREE.BufferGeometry[] = [] - - const curveL1 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.35 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.30 + (isLeft ? 0.05 : 0), 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveL2 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.15 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.1 + (isLeft ? 0.05 : 0), 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveL3 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0.15 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.1 - (isLeft ? 0.05 : 0), 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveL4 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0.30 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.35 - (isLeft ? 0.05 : 0), 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - if (!isLeft) { - const curveBar1 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.36, -0.4, 0), new THREE.Vector3(0.36, -0.4, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveBar2 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.36, 0, 0), new THREE.Vector3(0.36, 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveBar3 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.36, 0.4, 0), new THREE.Vector3(0.36, 0.4, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - geometries.push(new THREE.ExtrudeGeometry(shapeBar, { - steps: 1, - bevelEnabled: false, - extrudePath: curveBar1 - })) - geometries.push(new THREE.ExtrudeGeometry(shapeBar, { - steps: 1, - bevelEnabled: false, - extrudePath: curveBar2 - })) - geometries.push(new THREE.ExtrudeGeometry(shapeBar, { - steps: 1, - bevelEnabled: false, - extrudePath: curveBar3 - })) - } - - const geometryL1 = new THREE.ExtrudeGeometry(shape, { - steps: 1, - bevelEnabled: false, - extrudePath: curveL1 - }) - - const geometryL2 = new THREE.ExtrudeGeometry(shape, { - steps: 1, - bevelEnabled: false, - extrudePath: curveL2 - }) - - const geometryL3 = new THREE.ExtrudeGeometry(shape, { - steps: 1, - bevelEnabled: false, - extrudePath: curveL3 - }) - - const geometryL4 = new THREE.ExtrudeGeometry(shape, { - steps: 1, - bevelEnabled: false, - extrudePath: curveL4 - }) - - geometries.push(geometryL1, geometryL2, geometryL3, geometryL4) - - return mergeGeometries(geometries) - } - - // 创建clx叉的背板 - private static createClxForkBasePlate(): THREE.BufferGeometry { - // 606.5 - const width = 0.3 - const depth = 1.188 - const dd = 0.05 - const fdx = 0.06 - const fdy = 0.03 - - const shape = new THREE.Shape(); - shape.moveTo(-0.744 + 0.1, -0.5); - shape.lineTo(-0.744 + 0.1, 0.5); - shape.lineTo(-0.744 + 0.2, 0.5); - shape.lineTo(-0.744 + 0.2, -0.5); - shape.closePath() - - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 1.287, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const options = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curve // 设置挤出轨迹 - } - return new THREE.ExtrudeGeometry(shape, options) - } - - private static clxPedestalGeometry: THREE.BufferGeometry = null - private static clxPillarGeometry: THREE.BufferGeometry = null - private static clxForkGeometry: THREE.BufferGeometry = null - private static clxGemelGeometryL: THREE.BufferGeometry = null - private static clxGemelGeometryR: THREE.BufferGeometry = null - private static clxForkBasePlateGeometry: THREE.BufferGeometry = null +export default class Clx3dObject extends PtrObject { - constructor(item: ItemJson, option?: RendererCudOption) { - super() + constructor(item: ItemJson, viewport: Viewport, option?: RendererCudOption) { + super(item, viewport) - if (!Clx3dObject.clxPedestalGeometry) { - Clx3dObject.clxPedestalGeometry = Clx3dObject.createClxPedestal() + if (!Clx3DGraphics.clxPedestalGeometry) { + Clx3DGraphics.clxPedestalGeometry = Clx3DGraphics.createClxPedestal() } - const clxPedestalGeometry = Clx3dObject.clxPedestalGeometry + const clxPedestalGeometry = Clx3DGraphics.clxPedestalGeometry const clxPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}) const clxPedestalMesh = new THREE.Mesh(clxPedestalGeometry, clxPedestalMaterial) - if (!Clx3dObject.clxPillarGeometry) { - Clx3dObject.clxPillarGeometry = Clx3dObject.createClxPillar() + if (!Clx3DGraphics.clxPillarGeometry) { + Clx3DGraphics.clxPillarGeometry = Clx3DGraphics.createClxPillar() } - const clxPillarGeometry = Clx3dObject.clxPillarGeometry + const clxPillarGeometry = Clx3DGraphics.clxPillarGeometry const clxPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}) const clxPillarMesh = new THREE.Mesh(clxPillarGeometry, clxPillarMaterial) - if (!Clx3dObject.clxForkGeometry) { - Clx3dObject.clxForkGeometry = Clx3dObject.createClxFork() + if (!Clx3DGraphics.clxForkGeometry) { + Clx3DGraphics.clxForkGeometry = Clx3DGraphics.createClxFork() } - const clxForkGeometry = Clx3dObject.clxForkGeometry + const clxForkGeometry = Clx3DGraphics.clxForkGeometry const clxForkMaterial = new THREE.MeshPhongMaterial({color: 0xff444444}) const clxForkMesh = new THREE.Mesh(clxForkGeometry, clxForkMaterial) clxForkMesh.name = 'clxFork' - if (!Clx3dObject.clxGemelGeometryL) { - Clx3dObject.clxGemelGeometryL = Clx3dObject.createClxGemel(true) + if (!Clx3DGraphics.clxGemelGeometryL) { + Clx3DGraphics.clxGemelGeometryL = Clx3DGraphics.createClxGemel(true) } - const clxGemelGeometryL = Clx3dObject.clxGemelGeometryL + const clxGemelGeometryL = Clx3DGraphics.clxGemelGeometryL - if (!Clx3dObject.clxGemelGeometryR) { - Clx3dObject.clxGemelGeometryR = Clx3dObject.createClxGemel(false) + if (!Clx3DGraphics.clxGemelGeometryR) { + Clx3DGraphics.clxGemelGeometryR = Clx3DGraphics.createClxGemel(false) } - const clxGemelGeometryR = Clx3dObject.clxGemelGeometryR + const clxGemelGeometryR = Clx3DGraphics.clxGemelGeometryR const clxGemelMaterial = new THREE.MeshPhongMaterial({color: 0xff555555}) const clxGemelMeshL1 = new THREE.Mesh(clxGemelGeometryL, clxGemelMaterial) @@ -445,10 +55,10 @@ export default class Clx3dObject extends THREE.Object3D { const clxGemelMeshR2 = new THREE.Mesh(clxGemelGeometryR, clxGemelMaterial) clxGemelMeshR2.name = 'clxGemelMeshR2' - if (!Clx3dObject.clxForkBasePlateGeometry) { - Clx3dObject.clxForkBasePlateGeometry = Clx3dObject.createClxForkBasePlate() + if (!Clx3DGraphics.clxForkBasePlateGeometry) { + Clx3DGraphics.clxForkBasePlateGeometry = Clx3DGraphics.createClxForkBasePlate() } - const clxForkBasePlateGeometry = Clx3dObject.clxForkBasePlateGeometry + const clxForkBasePlateGeometry = Clx3DGraphics.clxForkBasePlateGeometry const clxForkBasePlateMesh = new THREE.Mesh(clxForkBasePlateGeometry, clxGemelMaterial) clxForkBasePlateMesh.name = 'clxForkBasePlateMesh' @@ -485,7 +95,7 @@ export default class Clx3dObject extends THREE.Object3D { this.add(clxForkBasePlateMesh) } - animationShowFork(z: number): Promise { + override animationShowFork(z: number): Promise { const clxFork = this.getObjectByName('clxFork') const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1') const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2') @@ -585,11 +195,11 @@ export default class Clx3dObject extends THREE.Object3D { } - animationHideFork(): Promise { + override animationHideFork(): Promise { return this.animationShowFork(0) } - animationUpFork(y: number, time?: number = 3): Promise { + override animationUpFork(y: number, time?: number = 3): Promise { const clxFork = this.getObjectByName('clxFork') const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1') const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2') @@ -660,7 +270,7 @@ export default class Clx3dObject extends THREE.Object3D { }) } - animationDownFork(): Promise { + override animationDownFork(): Promise { return this.animationUpFork(0) } diff --git a/src/modules/amr/ptr/clx/ClxPropertySetter.ts b/src/modules/amr/ptr/clx/ClxPropertySetter.ts index 5390c94..bebf4ab 100644 --- a/src/modules/amr/ptr/clx/ClxPropertySetter.ts +++ b/src/modules/amr/ptr/clx/ClxPropertySetter.ts @@ -13,6 +13,10 @@ const propertySetter: PropertySetter = { dataPath: 'dt.clxDepth', label: 'CLX深度', input: 'InputNumber', inputProps: {}, }, + { + dataPath: 'state', label: 'ptr控制', input: 'PtrController', + inputProps: {}, + }, ], }, }; diff --git a/src/modules/amr/ptr/clx/ClxRenderer.ts b/src/modules/amr/ptr/clx/ClxRenderer.ts index 58268eb..e10d10b 100644 --- a/src/modules/amr/ptr/clx/ClxRenderer.ts +++ b/src/modules/amr/ptr/clx/ClxRenderer.ts @@ -51,7 +51,7 @@ export default class ClxRenderer extends BaseRenderer { createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { // 创建平面几何体 - const group = new Clx3dObject(item, option) + const group = new Clx3dObject(item, this.tempViewport, option) group.name = ClxRenderer.POINT_NAME // 设置位置