8 changed files with 1283 additions and 1196 deletions
@ -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) |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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 |
||||
|
} |
||||
Loading…
Reference in new issue