yuliang 6 months ago
parent
commit
e35251c0ab
  1. 16
      src/editor/propEditors/PtrController.vue
  2. 828
      src/modules/amr/ptr/PtrObject.ts
  3. 781
      src/modules/amr/ptr/cl2/Cl23dObject.ts
  4. 2
      src/modules/amr/ptr/cl2/Cl2Renderer.ts
  5. 400
      src/modules/amr/ptr/clx/Clx3DGraphics.ts
  6. 446
      src/modules/amr/ptr/clx/Clx3dObject.ts
  7. 4
      src/modules/amr/ptr/clx/ClxPropertySetter.ts
  8. 2
      src/modules/amr/ptr/clx/ClxRenderer.ts

16
src/editor/propEditors/PtrController.vue

@ -6,7 +6,7 @@ import { CopyDocument, Delete } from "@element-plus/icons-vue";
import { Typeof } from "@ease-forge/shared"; import { Typeof } from "@ease-forge/shared";
import type Viewport from "@/core/engine/Viewport"; import type Viewport from "@/core/engine/Viewport";
import {BayEditorProps} from "@/editor/propEditors/BayEditor.vue"; import {BayEditorProps} from "@/editor/propEditors/BayEditor.vue";
import Cl23dObject from "@/modules/cl2/Cl23dObject"; import PtrObject from "@/modules/amr/ptr/PtrObject";
defineOptions({ defineOptions({
name: 'PtrController', name: 'PtrController',
@ -18,9 +18,16 @@ function start() {
const viewport: Viewport = window['viewport'] const viewport: Viewport = window['viewport']
// viewport.envManager.client.publish('/agv_robot/status', '') // viewport.envManager.client.publish('/agv_robot/status', '')
const item = viewport.state.selectedItem const item = viewport.state.selectedItem
const object3D: Cl23dObject = viewport.state.selectedObject as Cl23dObject const ptrObject: PtrObject = viewport.state.selectedObject as PtrObject
console.log(item, object3D) ptrObject.boot()
object3D.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() {
<template> <template>
<div class="ptr-controller"> <div class="ptr-controller">
<el-button @click="start">启动</el-button> <el-button @click="start">启动</el-button>
<el-button @click="stop">关闭</el-button>
</div> </div>
</template> </template>

828
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<any>[] = []
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<AmrMsg20148>(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<AmrMsg20147>(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<AmrMsg20149>(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<AmrMsg20150>(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<AmrMsg20250>(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<AmrMsg20020>(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<AmrMsg20010>(content)
this.sendMessage(m20010)
}
send20011(content: AmrMsg20011<any>) {
const m20011 = new AmrMsg<AmrMsg20011<any>>(content)
this.sendMessage(m20011)
}
send20020(content: AmrMsg20020) {
const m20020 = new AmrMsg<AmrMsg20020>(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<AmrMsg20060>(content)
this.sendMessage(m20060)
}
send20250(content: AmrMsg20250) {
this.sendMessage(new AmrMsg<AmrMsg20250>(content))
}
subscribeMessage(topic: string) {
worldModel.envManager.client.subscribe(topic, {qos: 0})
}
sendMessage(msg: AmrMsg<any>) {
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<AmrMsg20100>(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<AmrMsg20050>(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<AmrMsg10050>) {
if (this.sendMessageQueue.length > 0 && data.content.SeqNo === this.sendMessageQueue[0].content.SeqNo) {
this.mqRetryTimeCount = 0
this.sendMessageQueue.shift()
}
}
handle10060Message(data: AmrMsg<AmrMsg10060>) {
this.mqRetryInterval = data.content.MqRetryTime
this.heartBeatInterval = data.content.HeartBeat
this.TaskMode = 0
}
// 处理状态查询
handle10110Message(data: AmrMsg<AmrMsg10110>) {
this.send20060()
}
// 取消任务
handle10120Message(data: AmrMsg<AmrMsg10120>) {
}
/*==========真车消息处理============*/
// 计算逻辑方向
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<void> {
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<void> {
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<void>{
return null
};
override animationHideFork(): Promise<void> {
return null
}
override animationUpFork(y: number, time?: number = 3): Promise<void> {
return null
}
override animationDownFork(): Promise<void> {
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)
}
}

781
src/modules/amr/ptr/cl2/Cl23dObject.ts

@ -24,124 +24,13 @@ import {
type LogicDirection, TaskCompletedData, TaskModeChangeData, TaskStatusChangeData, TaskTypeChangeData, AmrMsg20010 type LogicDirection, TaskCompletedData, TaskModeChangeData, TaskStatusChangeData, TaskTypeChangeData, AmrMsg20010
} from "@/core/manager/amr/AmrMessageDefine"; } from "@/core/manager/amr/AmrMessageDefine";
import {worldModel} from "@/core/manager/WorldModel"; import {worldModel} from "@/core/manager/WorldModel";
import PtrObject from "../PtrObject";
import Viewport from "@/core/engine/Viewport";
export default class Cl23dObject extends PtrObject {
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
private _cl2Entity: Cl2Entity = null 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<any>[] = []
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 { public get cl2Entity(): Cl2Entity {
if (!this._cl2Entity) { if (!this._cl2Entity) {
const cl2: Cl2Entity = Model.getCl2(this.item.id) as 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 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) { if (!Cl23DGraphics.ptrPedestalGeometry) {
Cl23DGraphics.ptrPedestalGeometry = Cl23DGraphics.createPtrPedestal() Cl23DGraphics.ptrPedestalGeometry = Cl23DGraphics.createPtrPedestal()
} }
@ -187,661 +72,13 @@ export default class Cl23dObject extends THREE.Object3D {
groupPillar.add(ptrPillarMesh) groupPillar.add(ptrPillarMesh)
groupPillar.add(ptrForkMesh) groupPillar.add(ptrForkMesh)
this.add(groupPillar) 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<AmrMsg20148>(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<AmrMsg20147>(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<AmrMsg20149>(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<AmrMsg20150>(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<AmrMsg20250>(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<AmrMsg20020>(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<AmrMsg20010>(content)
this.sendMessage(m20010)
}
send20011(content: AmrMsg20011<any>) {
const m20011 = new AmrMsg<AmrMsg20011<any>>(content)
this.sendMessage(m20011)
}
send20020(content: AmrMsg20020) {
const m20020 = new AmrMsg<AmrMsg20020>(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<AmrMsg20060>(content)
this.sendMessage(m20060)
}
send20250(content: AmrMsg20250) {
this.sendMessage(new AmrMsg<AmrMsg20250>(content))
}
subscribeMessage(topic: string) {
worldModel.envManager.client.subscribe(topic, {qos: 0})
}
sendMessage(msg: AmrMsg<any>) {
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<AmrMsg20100>(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<AmrMsg20050>(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<AmrMsg10050>) {
if (this.sendMessageQueue.length > 0 && data.content.SeqNo === this.sendMessageQueue[0].content.SeqNo) {
this.mqRetryTimeCount = 0
this.sendMessageQueue.shift()
}
}
handle10060Message(data: AmrMsg<AmrMsg10060>) {
this.mqRetryInterval = data.content.MqRetryTime
this.heartBeatInterval = data.content.HeartBeat
this.TaskMode = 0
}
// 处理状态查询
handle10110Message(data: AmrMsg<AmrMsg10110>) {
this.send20060()
}
// 取消任务
handle10120Message(data: AmrMsg<AmrMsg10120>) {
}
/*==========真车消息处理============*/
// 计算逻辑方向
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<void> {
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<void> {
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<void> { override animationShowFork(z: number): Promise<void> {
const ptrPillar = this.getObjectByName('ptrPillar') const ptrPillar = this.getObjectByName('ptrPillar')
const time = 3 const time = 3
@ -857,11 +94,11 @@ export default class Cl23dObject extends THREE.Object3D {
} }
animationHideFork(): Promise<void> { override animationHideFork(): Promise<void> {
return this.animationShowFork(0) return this.animationShowFork(0)
} }
animationUpFork(y: number, time?: number = 3): Promise<void> { override animationUpFork(y: number, time?: number = 3): Promise<void> {
const ptrFork = this.getObjectByName('ptrFork') const ptrFork = this.getObjectByName('ptrFork')
const ptrPillar = this.getObjectByName('ptrPillar') const ptrPillar = this.getObjectByName('ptrPillar')
const pz = ptrPillar.position.z const pz = ptrPillar.position.z
@ -895,7 +132,7 @@ export default class Cl23dObject extends THREE.Object3D {
}) })
} }
animationDownFork(): Promise<void> { override animationDownFork(): Promise<void> {
return this.animationUpFork(0) return this.animationUpFork(0)
} }
} }

2
src/modules/amr/ptr/cl2/Cl2Renderer.ts

@ -55,7 +55,7 @@ export default class PtrRenderer extends BaseRenderer {
return null return null
} }
const group = new Cl23dObject(item, option) const group = new Cl23dObject(item, this.tempViewport, option)
group.name = PtrRenderer.POINT_NAME group.name = PtrRenderer.POINT_NAME
// 设置位置 // 设置位置

400
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
}

446
src/modules/amr/ptr/clx/Clx3dObject.ts

@ -3,437 +3,47 @@ import {CSG} from 'three-csg-ts'
import gsap from 'gsap' import gsap from 'gsap'
//@ts-ignore //@ts-ignore
import {mergeGeometries} from 'three/addons/utils/BufferGeometryUtils.js' 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 { export default class Clx3dObject extends PtrObject {
// 创建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
constructor(item: ItemJson, option?: RendererCudOption) { constructor(item: ItemJson, viewport: Viewport, option?: RendererCudOption) {
super() super(item, viewport)
if (!Clx3dObject.clxPedestalGeometry) { if (!Clx3DGraphics.clxPedestalGeometry) {
Clx3dObject.clxPedestalGeometry = Clx3dObject.createClxPedestal() Clx3DGraphics.clxPedestalGeometry = Clx3DGraphics.createClxPedestal()
} }
const clxPedestalGeometry = Clx3dObject.clxPedestalGeometry const clxPedestalGeometry = Clx3DGraphics.clxPedestalGeometry
const clxPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}) const clxPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca})
const clxPedestalMesh = new THREE.Mesh(clxPedestalGeometry, clxPedestalMaterial) const clxPedestalMesh = new THREE.Mesh(clxPedestalGeometry, clxPedestalMaterial)
if (!Clx3dObject.clxPillarGeometry) { if (!Clx3DGraphics.clxPillarGeometry) {
Clx3dObject.clxPillarGeometry = Clx3dObject.createClxPillar() Clx3DGraphics.clxPillarGeometry = Clx3DGraphics.createClxPillar()
} }
const clxPillarGeometry = Clx3dObject.clxPillarGeometry const clxPillarGeometry = Clx3DGraphics.clxPillarGeometry
const clxPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}) const clxPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956})
const clxPillarMesh = new THREE.Mesh(clxPillarGeometry, clxPillarMaterial) const clxPillarMesh = new THREE.Mesh(clxPillarGeometry, clxPillarMaterial)
if (!Clx3dObject.clxForkGeometry) { if (!Clx3DGraphics.clxForkGeometry) {
Clx3dObject.clxForkGeometry = Clx3dObject.createClxFork() Clx3DGraphics.clxForkGeometry = Clx3DGraphics.createClxFork()
} }
const clxForkGeometry = Clx3dObject.clxForkGeometry const clxForkGeometry = Clx3DGraphics.clxForkGeometry
const clxForkMaterial = new THREE.MeshPhongMaterial({color: 0xff444444}) const clxForkMaterial = new THREE.MeshPhongMaterial({color: 0xff444444})
const clxForkMesh = new THREE.Mesh(clxForkGeometry, clxForkMaterial) const clxForkMesh = new THREE.Mesh(clxForkGeometry, clxForkMaterial)
clxForkMesh.name = 'clxFork' clxForkMesh.name = 'clxFork'
if (!Clx3dObject.clxGemelGeometryL) { if (!Clx3DGraphics.clxGemelGeometryL) {
Clx3dObject.clxGemelGeometryL = Clx3dObject.createClxGemel(true) Clx3DGraphics.clxGemelGeometryL = Clx3DGraphics.createClxGemel(true)
} }
const clxGemelGeometryL = Clx3dObject.clxGemelGeometryL const clxGemelGeometryL = Clx3DGraphics.clxGemelGeometryL
if (!Clx3dObject.clxGemelGeometryR) { if (!Clx3DGraphics.clxGemelGeometryR) {
Clx3dObject.clxGemelGeometryR = Clx3dObject.createClxGemel(false) Clx3DGraphics.clxGemelGeometryR = Clx3DGraphics.createClxGemel(false)
} }
const clxGemelGeometryR = Clx3dObject.clxGemelGeometryR const clxGemelGeometryR = Clx3DGraphics.clxGemelGeometryR
const clxGemelMaterial = new THREE.MeshPhongMaterial({color: 0xff555555}) const clxGemelMaterial = new THREE.MeshPhongMaterial({color: 0xff555555})
const clxGemelMeshL1 = new THREE.Mesh(clxGemelGeometryL, clxGemelMaterial) 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) const clxGemelMeshR2 = new THREE.Mesh(clxGemelGeometryR, clxGemelMaterial)
clxGemelMeshR2.name = 'clxGemelMeshR2' clxGemelMeshR2.name = 'clxGemelMeshR2'
if (!Clx3dObject.clxForkBasePlateGeometry) { if (!Clx3DGraphics.clxForkBasePlateGeometry) {
Clx3dObject.clxForkBasePlateGeometry = Clx3dObject.createClxForkBasePlate() Clx3DGraphics.clxForkBasePlateGeometry = Clx3DGraphics.createClxForkBasePlate()
} }
const clxForkBasePlateGeometry = Clx3dObject.clxForkBasePlateGeometry const clxForkBasePlateGeometry = Clx3DGraphics.clxForkBasePlateGeometry
const clxForkBasePlateMesh = new THREE.Mesh(clxForkBasePlateGeometry, clxGemelMaterial) const clxForkBasePlateMesh = new THREE.Mesh(clxForkBasePlateGeometry, clxGemelMaterial)
clxForkBasePlateMesh.name = 'clxForkBasePlateMesh' clxForkBasePlateMesh.name = 'clxForkBasePlateMesh'
@ -485,7 +95,7 @@ export default class Clx3dObject extends THREE.Object3D {
this.add(clxForkBasePlateMesh) this.add(clxForkBasePlateMesh)
} }
animationShowFork(z: number): Promise<void> { override animationShowFork(z: number): Promise<void> {
const clxFork = this.getObjectByName('clxFork') const clxFork = this.getObjectByName('clxFork')
const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1') const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1')
const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2') const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2')
@ -585,11 +195,11 @@ export default class Clx3dObject extends THREE.Object3D {
} }
animationHideFork(): Promise<void> { override animationHideFork(): Promise<void> {
return this.animationShowFork(0) return this.animationShowFork(0)
} }
animationUpFork(y: number, time?: number = 3): Promise<void> { override animationUpFork(y: number, time?: number = 3): Promise<void> {
const clxFork = this.getObjectByName('clxFork') const clxFork = this.getObjectByName('clxFork')
const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1') const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1')
const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2') const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2')
@ -660,7 +270,7 @@ export default class Clx3dObject extends THREE.Object3D {
}) })
} }
animationDownFork(): Promise<void> { override animationDownFork(): Promise<void> {
return this.animationUpFork(0) return this.animationUpFork(0)
} }

4
src/modules/amr/ptr/clx/ClxPropertySetter.ts

@ -13,6 +13,10 @@ const propertySetter: PropertySetter = {
dataPath: 'dt.clxDepth', label: 'CLX深度', input: 'InputNumber', dataPath: 'dt.clxDepth', label: 'CLX深度', input: 'InputNumber',
inputProps: {}, inputProps: {},
}, },
{
dataPath: 'state', label: 'ptr控制', input: 'PtrController',
inputProps: {},
},
], ],
}, },
}; };

2
src/modules/amr/ptr/clx/ClxRenderer.ts

@ -51,7 +51,7 @@ export default class ClxRenderer extends BaseRenderer {
createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { 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 group.name = ClxRenderer.POINT_NAME
// 设置位置 // 设置位置

Loading…
Cancel
Save