From ce90fb5b44cca85127bbd70371ebba69b4e0cfe9 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Tue, 17 Jun 2025 20:09:41 +0800 Subject: [PATCH] =?UTF-8?q?CL2=20Task=20=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/base/BaseItemEntity.ts | 287 +++++++++++++++++++++++++++-- src/core/base/BaseRenderer.ts | 24 ++- src/core/base/Carry.ts | 1 + src/core/base/TaskQueue.ts | 98 ++++++++++ src/core/manager/EntityManager.ts | 2 +- src/core/manager/InstanceMeshManager.ts | 53 +++++- src/core/manager/TaskManager.ts | 13 +- src/core/manager/WorldModel.ts | 4 +- src/core/script/ModelManager.ts | 9 + src/example/example1.js | 54 ++++-- src/modules/cl2/Cl2Entity.ts | 72 ++++++++ src/modules/cl2/Cl2Interaction.ts | 22 +++ src/modules/cl2/Cl2PropertySetter.ts | 20 ++ src/modules/cl2/Cl2Renderer.ts | 312 +++++++++++++++++++++++++++++++ src/modules/cl2/index.ts | 15 ++ src/modules/gstore/GstoreRenderer.ts | 23 +++ src/modules/ptr/PtrEntity.ts | 5 - src/modules/ptr/PtrInteraction.ts | 22 --- src/modules/ptr/PtrPropertySetter.ts | 20 -- src/modules/ptr/PtrRenderer.ts | 315 -------------------------------- src/modules/ptr/index.ts | 15 -- src/types/ScriptSupport.d.ts | 10 + src/types/model.d.ts | 178 +++++++++++++++++- 23 files changed, 1144 insertions(+), 430 deletions(-) create mode 100644 src/core/base/Carry.ts create mode 100644 src/core/base/TaskQueue.ts create mode 100644 src/modules/cl2/Cl2Entity.ts create mode 100644 src/modules/cl2/Cl2Interaction.ts create mode 100644 src/modules/cl2/Cl2PropertySetter.ts create mode 100644 src/modules/cl2/Cl2Renderer.ts create mode 100644 src/modules/cl2/index.ts delete mode 100644 src/modules/ptr/PtrEntity.ts delete mode 100644 src/modules/ptr/PtrInteraction.ts delete mode 100644 src/modules/ptr/PtrPropertySetter.ts delete mode 100644 src/modules/ptr/PtrRenderer.ts delete mode 100644 src/modules/ptr/index.ts diff --git a/src/core/base/BaseItemEntity.ts b/src/core/base/BaseItemEntity.ts index a048ffe..d313181 100644 --- a/src/core/base/BaseItemEntity.ts +++ b/src/core/base/BaseItemEntity.ts @@ -1,26 +1,285 @@ import * as THREE from 'three' +import gsap from 'gsap' +import type { Object3DLike } from '@/types/ModelTypes.ts' +import Viewport from '@/core/engine/Viewport.ts' +import TaskQueue, { type Task } from '@/core/base/TaskQueue.ts' +import type { MeshWrap } from '@/core/manager/InstanceMeshManager.ts' +import { getRenderer } from '@/core/manager/ModuleManager.ts' /** - * BaseEntity class - * Provides a base for managing logistics unit entities. + * 物流实体基类 */ -export default abstract class BaseEntity { - protected itemJson!: ItemJson - protected objects!: THREE.Object3D[] +export default abstract class BaseEntity implements EntityIf, Loadable, Carry, Walk, ForkArm, LiftingArm { + protected readonly viewport: Viewport + protected readonly taskQueue: TaskQueue = new TaskQueue() + readonly id: string + readonly item: ItemJson + readonly object: THREE.Object3D + readonly t: string + + isArmExtended: boolean = false + isArmRaised: boolean = false + isCarrying: boolean = false + isLoading: boolean = false + isUnloading: boolean = false + isWalking: boolean = false + isPaused: boolean = false + + constructor(viewport: Viewport, id: string) { + this.viewport = viewport + this.id = id + this.item = viewport.find(id) + this.object = viewport.entityManager.findObjectById(id) as THREE.Object3D + this.t = this.item.t + } + + // 帮助方法:获取机械臂对象 + abstract getArmObject(): THREE.Object3D | undefined + + // 帮助方法:获取机械臂立柱 + abstract getArmPillar(): THREE.Object3D | undefined + + // 待执行任务总数 + get taskCount(): number { + return this.taskQueue.length + } + + // 是否正在执行任务 + get taskIsRunning(): boolean { + return this.taskQueue.isExecuting + } + + // 取消所有任务 + cancelTask(): void { + this.taskQueue.cancelAll() + } + + // 暂停任务 + taskPause(): void { + this.isPaused = true + this.taskQueue.pause() + } + + // 恢复任务 + taskResume(): void { + this.isPaused = false + this.taskQueue.resume() + } + + // 开始执行任务 + taskStartRun(): void { + this.taskQueue.start() + } + + // 创建基础任务 + protected createTask(type: string, execute: () => Promise, cancel?: () => void): T { + return { type, execute, cancel, isCancelable: true } as T + } + + // 上 + addArmRaise(height: number): void { + this.taskQueue.add(this.createTask('ARM_RAISE', async () => { + return new Promise(resolve => { + const arm = this.getArmObject() + if (!arm) return resolve() + + gsap.to(arm.position, { + y: height, + duration: 3, + ease: 'sine.inOut', + onComplete: resolve + }) + }) + }, () => { + // 取消动画 + gsap.killTweensOf(this.getArmObject()?.position) + })) + } + + // 下 + addArmLower(): void { + this.taskQueue.add(this.createTask('ARM_LOWER', async () => { + return new Promise(resolve => { + const arm = this.getArmObject() + if (!arm) return resolve() + + gsap.to(arm.position, { + y: 0, + duration: 3, + ease: 'sine.inOut', + onComplete: resolve + }) + }) + }, () => { + gsap.killTweensOf(this.getArmObject()?.position) + })) + } + + // 伸 + addArmExtender(): void { + this.taskQueue.add(this.createTask('ARM_EXTEND', async () => { + return new Promise(resolve => { + const pillar = this.getArmPillar() + if (!pillar) return resolve() + + gsap.to(pillar.position, { + z: -1.2, + duration: 3, + ease: 'sine.inOut', + onComplete: () => { + this.isArmExtended = true + resolve() + } + }) + }) + }, () => { + gsap.killTweensOf(this.getArmPillar()?.position) + })) + } + + // 缩 + addArmRetractor(): void { + this.taskQueue.add(this.createTask('ARM_RETRACT', async () => { + return new Promise(resolve => { + const pillar = this.getArmPillar() + if (!pillar) return resolve() + + gsap.to(pillar.position, { + z: 0, + duration: 3, + ease: 'sine.inOut', + onComplete: () => { + this.isArmExtended = false + resolve() + } + }) + }) + }, () => { + gsap.killTweensOf(this.getArmPillar()?.position) + })) + } + + // 装 + addLoad(item: string): void { + this.taskQueue.add(this.createTask('LOAD', async () => { + // 实际业务中应包含装载逻辑 + this.isCarrying = true + throw new Error(`装载物品 ${item} 的逻辑未实现`) + })) + } + + // 卸 + addUnload(item: string, rack: string, bay?: number, level?: number): void { + this.taskQueue.add(this.createTask('UNLOAD', async () => { + // 实际业务中应包含卸载逻辑 + this.isCarrying = false + throw new Error(`装载物品 ${item} 的逻辑未实现`) + })) + } + + // 转 + addRotation(angle: number): void { + this.taskQueue.add(this.createTask('ROTATION', async () => { + return new Promise(resolve => { + const time = Math.abs(angle - THREE.MathUtils.radToDeg(this.object.rotation.y)) / 45 + const duration = Math.max(0.5, time) + + gsap.to(this.object.rotation, { + y: THREE.MathUtils.degToRad(angle), + duration, + ease: 'none', + onComplete: resolve + }) + }) + }, () => { + gsap.killTweensOf(this.object.rotation) + })) + } + + // 走 + addTravel(pos: Vector3IF | string): void { + this.taskQueue.add(this.createTask('TRAVEL', async () => { + return new Promise(resolve => { + const fromPos = this.object.position + + let position: Vector3IF + if (pos instanceof String || typeof pos === 'string') { + pos = Model.getPositionByEntityId(pos as string) + } + + const toPos = new THREE.Vector3(pos.x, pos.y, pos.z) + const distance = fromPos.distanceTo(toPos) + const speed = 2 // 2m/s + const duration = Math.max(1.5, distance / speed) + + this.isWalking = true + + gsap.to(this.object.position, { + x: toPos.x, + y: toPos.y, + z: toPos.z, + duration, + ease: 'sine.inOut', + onComplete: () => { + this.isWalking = false + resolve() + } + }) + }) + }, () => { + this.isWalking = false + gsap.killTweensOf(this.object.position) + })) + } /** - * Sets the `ItemJson` data for the entity. - * @param itemJson - The `ItemJson` data to set. + * 拾取物品 */ - setItem(itemJson: ItemJson): void { - this.itemJson = itemJson + pickupItem(id: string): THREE.Object3D { + // 找到物品所在的地方, 删除他 + const item = this.viewport.entityManager.findItemById(id) + const wrap = this.viewport.entityManager.findObjectById(id) as MeshWrap + + if (wrap.type !== 'MeshWrap') { + throw new Error(`无法拾取物品 ${id},它不是一个有效的 MeshWrap`) + } + item.dt.storeAt = { + item: this.id + } + const mesh = wrap.manager.wrapToObject3D(wrap) + this.viewport.entityManager.replaceObject(id, mesh) + return mesh } /** - * Sets the `THREE.Object3D` object for the entity. - * @param object3D - The `THREE.Object3D` object to set. + * 卸下物品 */ - setObjects(objects: THREE.Object3D[]): void { - this.objects = objects + dropItem(item: ItemJson, storeItemId: string, bay?: number, level?: number, cell?: number): void { + item.dt.storeAt = { + item: storeItemId, + bay: bay, + level: level, + cell: cell + } + const rack = Model.find(storeItemId) + const rackRenderer = getRenderer(rack.t) + const itemRenderer = getRenderer(item.t) + if (!rackRenderer) { + throw new Error(`Renderer for type ${item.t} not found`) + } + if (!itemRenderer) { + throw new Error(`Renderer for type ${item.t} not found`) + } + + const { position, rotation } = rackRenderer.getStorePlacement(this.viewport, item) + if (!position || !rotation) { + throw new Error(`无法获取物品 ${item.id} 的存储位置`) + } + + this.getArmObject().clear() + + itemRenderer.tempViewport = this.viewport + itemRenderer.createOrUpdatePointForRuntime(item, position, rotation) + itemRenderer.tempViewport = null } -} \ No newline at end of file +} diff --git a/src/core/base/BaseRenderer.ts b/src/core/base/BaseRenderer.ts index 7a9f3bb..cb22f7a 100644 --- a/src/core/base/BaseRenderer.ts +++ b/src/core/base/BaseRenderer.ts @@ -119,6 +119,11 @@ export default abstract class BaseRenderer { return point } + createOrUpdatePointForRuntime(item: ItemJson, position: THREE.Vector3, rotation: THREE.Quaternion): Object3DLike { + const option = { isRuntime: true, position, rotation } + return this.createPointForEntity(item, option) + } + /** * 创建一个点 * @param item 点的定义 @@ -127,7 +132,16 @@ export default abstract class BaseRenderer { createPoint(item: ItemJson, option?: RendererCudOption): Object3DLike { // 由基础类创造一个属于自己的点演示 const point = this.createPointBasic(item, option) - point.setMatrix4(getMatrixFromTf(item.tf)) + let matrix = getMatrixFromTf(item.tf) + if (option?.position instanceof THREE.Vector3 && option?.rotation instanceof THREE.Quaternion) { + matrix = new THREE.Matrix4() + matrix.compose( + option.position, + option.rotation, + new THREE.Vector3(item.tf[2][0], item.tf[2][1], item.tf[2][2]) + ) + } + point.setMatrix4(matrix) // point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) // @@ -250,6 +264,14 @@ export default abstract class BaseRenderer { } /** + * 获取物品存放位 + * 获取物品存储到地堆货位中,返回可存放的位置和角度一个 Position 和 Rotation + */ + getStorePlacement(viewport: Viewport, item: ItemJson): { position: THREE.Vector3, rotation: THREE.Quaternion } { + throw new Error(' 不支持库存物品的添加. t=' + this.itemTypeName) + } + + /** * 更新一根线 * @param start 起点 * @param end 终点 diff --git a/src/core/base/Carry.ts b/src/core/base/Carry.ts new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/core/base/Carry.ts @@ -0,0 +1 @@ + diff --git a/src/core/base/TaskQueue.ts b/src/core/base/TaskQueue.ts new file mode 100644 index 0000000..0537221 --- /dev/null +++ b/src/core/base/TaskQueue.ts @@ -0,0 +1,98 @@ +/** + * 任务类型定义 + */ +export interface Task { + type: string + + execute(): Promise + + cancel?(): void + + isCancelable?: boolean +} + +/** + * 任务队列管理类 + */ +export default class TaskQueue { + private queue: Task[] = [] + private currentTask: Task | null = null + private isRunning = false + private isPaused = false + private onTaskComplete: ((task: Task) => void) | null = null + + constructor() { + } + + get length(): number { + return this.queue.length + (this.currentTask ? 1 : 0) + } + + get isExecuting(): boolean { + return this.isRunning && !this.isPaused + } + + add(task: Task): void { + this.queue.push(task) + } + + clear(): void { + this.queue = [] + } + + pause(): void { + this.isPaused = true + if (this.currentTask && this.currentTask.cancel) { + this.currentTask.cancel() + } + } + + resume(): void { + this.isPaused = false + if (this.currentTask) { + this.executeNextTask() + } + } + + cancelAll(): void { + this.isRunning = false + this.isPaused = false + this.queue = [] + if (this.currentTask && this.currentTask.cancel) { + this.currentTask.cancel() + } + this.currentTask = null + if (this.onTaskComplete) { + this.onTaskComplete(this.currentTask || { type: 'CANCELLED' } as Task) + } + } + + start(onTaskComplete?: (task: Task) => void): void { + this.onTaskComplete = onTaskComplete + if (!this.isRunning) { + this.isRunning = true + this.executeNextTask() + } + } + + private async executeNextTask(): Promise { + if (this.isPaused || this.queue.length === 0) { + return + } + + this.currentTask = this.queue.shift()! + + try { + await this.currentTask.execute() + if (this.onTaskComplete) { + this.onTaskComplete(this.currentTask) + } + this.currentTask = null + this.executeNextTask() + } catch (error) { + console.error('Task execution failed:', error) + this.currentTask = null + this.executeNextTask() + } + } +} diff --git a/src/core/manager/EntityManager.ts b/src/core/manager/EntityManager.ts index 1838b07..8714e54 100644 --- a/src/core/manager/EntityManager.ts +++ b/src/core/manager/EntityManager.ts @@ -500,7 +500,7 @@ export default class EntityManager { if (!linkStartPointId) { return } - return getFreezeDeep(this.___entityMap.get(linkStartPointId)) + return this.___entityMap.get(linkStartPointId) } getObjectByCanvasMouse(event: MouseEvent): Object3DLike[] { diff --git a/src/core/manager/InstanceMeshManager.ts b/src/core/manager/InstanceMeshManager.ts index cb20cc7..d3344d3 100644 --- a/src/core/manager/InstanceMeshManager.ts +++ b/src/core/manager/InstanceMeshManager.ts @@ -12,8 +12,8 @@ export default class InstanceMeshManager { public readonly blockCapacity: number public readonly blocks: InstanceMeshBlock[] = [] - private readonly geometry: THREE.BufferGeometry - private readonly material: THREE.Material + public readonly geometry: THREE.BufferGeometry + public readonly material: THREE.Material private readonly dummy: THREE.Object3D = new THREE.Object3D() @@ -159,6 +159,53 @@ export default class InstanceMeshManager { } this.__uuidMap.clear() } + + /** + * 将 MeshWrap 变成独立的 Object3D + */ + wrapToObject3D(object: MeshWrap): THREE.Object3D { + if (!(object instanceof MeshWrap)) { + console.warn('InstanceMeshManager: Only THREE.Mesh can be wrapped') + return null + } + const wrap = object as MeshWrap + const matrix = wrap.matrix.clone() + + const mesh = new THREE.Mesh(this.geometry, this.material) + mesh.uuid = wrap.uuid + mesh.name = wrap.name + mesh.visible = wrap.visible + mesh.userData = wrap.userData + matrix.decompose(mesh.position, mesh.quaternion, mesh.scale) + mesh.updateMatrix() + + wrap.dispose() + return mesh + } + + /** + * 将 Object3D 包装到 InstancedMesh 中 + */ + object3DToWrap(object: THREE.Object3D): MeshWrap { + if (!(object instanceof THREE.Mesh)) { + console.warn('InstanceMeshManager: Only THREE.Mesh can be wrapped') + return null + } + + const entityId = object.uuid || system.createUUID() + const wrap = this.create(entityId, object.userData) + + // 设置初始矩阵 + object.updateMatrixWorld(true) + const matrix = new THREE.Matrix4().copy(object.matrixWorld) + wrap.setMatrix4(matrix) + + // 设置其他属性 + wrap.name = object.name || entityId + wrap.visible = object.visible + + return wrap + } } export class MeshWrap { @@ -174,6 +221,8 @@ export class MeshWrap { name: string visible: boolean + type = 'MeshWrap' + get position(): THREE.Vector3 { const m = new THREE.Matrix4() this.manager.blocks[this.blockIndex].instancedMesh.getMatrixAt(this.meshIndex, m) diff --git a/src/core/manager/TaskManager.ts b/src/core/manager/TaskManager.ts index b20eef8..9db1540 100644 --- a/src/core/manager/TaskManager.ts +++ b/src/core/manager/TaskManager.ts @@ -1,16 +1,5 @@ -// 任务 API -// TaskSequence ts = TaskSequence.create(agv) -// ts.addTask('TASKTYPE_LOAD', item, queue); -// ts.addTask('TASKTYPE_TRAVEL', "entityId"); -// ts.addTask('TASKTYPE_ROTATION', 90); -// ts.addTask('TASKTYPE_UNLOAD', item, rack); -// ts.addTask('TASKTYPE_UNLOAD', item, rack, 1, 3); -// -// ts.dispatch(); - -// import {Object3DLike} from "@/types/ModelTypes"; -import gsap from 'gsap' import * as THREE from 'three' +import gsap from 'gsap' import Viewport from '@/core/engine/Viewport' export default class TaskManager implements TaskManagerIF { diff --git a/src/core/manager/WorldModel.ts b/src/core/manager/WorldModel.ts index 5d80020..0a0eebb 100644 --- a/src/core/manager/WorldModel.ts +++ b/src/core/manager/WorldModel.ts @@ -10,7 +10,7 @@ import Pallet from '@/modules/pallet' import Tote from '@/modules/tote' import Agv1 from '@/modules/agv1' import Carton from '@/modules/carton' -import Ptr from '@/modules/ptr' +import Cl2 from '@/modules/cl2' import Clx from '@/modules/clx' import Charger from '@/modules/charger' import StateManager from '@/core/manager/StateManager.ts' @@ -93,7 +93,7 @@ export default class WorldModel { Pallet, Tote, Carton, - Ptr, + Cl2, Clx, Agv1, Charger diff --git a/src/core/script/ModelManager.ts b/src/core/script/ModelManager.ts index e313f1c..709e89e 100644 --- a/src/core/script/ModelManager.ts +++ b/src/core/script/ModelManager.ts @@ -4,10 +4,19 @@ import * as THREE from 'three' import { getMatrixFromTf } from '@/core/ModelUtils.ts' import type { Object3DLike } from '@/types/ModelTypes.ts' import TaskManager from '../manager/TaskManager.ts' +import Cl2Entity from '@/modules/cl2/Cl2Entity.ts' export default class ModelManager implements IControls, Model { private viewport: Viewport + getCl2(id: string): Cl2If { + return new Cl2Entity(this.viewport, id) + } + + getClx(id: string): ClxIf { + throw new Error('Method not implemented.') + } + createTask(agv: object): TaskManager { if (TaskManager.taskMap.has(agv)) { return TaskManager.taskMap.get(agv) diff --git a/src/example/example1.js b/src/example/example1.js index 3f0629b..4f635e2 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -128,13 +128,13 @@ export default { dt: { in: [], out: [], center: [], palletWidth: 1, palletDepth: 1.2 } }, { id: 'ptr1', - t: 'ptr', + t: 'cl2', v: true, tf: [[0.75, 0.075, 2], [0, 0, 0], [1.5, 1.2, 0.1]], dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 } }, { id: 'ptr2', - t: 'ptr', + t: 'cl2', v: true, tf: [[3, 0.075, 2], [0, 0, 0], [1.5, 1.2, 0.1]], dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 } @@ -203,7 +203,7 @@ export default { v: true, tf: [[7.1, 0.1, 2.865], [0, 90, 0], [1, 0.01, 1]], dt: { in: [], out: [], center: [], storeWidth: 1.1, storeDepth: 1.3 } - }, { id: '1hAaZ1xtvukZowAKeWAcqs', t: 'gstore', v: true, tf: [[7.1, 0.1, 4.35], [0, 90, 0], [1, 0.01, 1]], dt: { in: [], out: [], center: [], storeWidth: 1.1, storeDepth: 1.3 } }, { + }, { id: '1hAaZ1xtvukZowAKeWAcqs', t: 'gstore', v: true, tf: [[7.1, 0.1, 4.35], [0, 0, 0], [1, 0.01, 1]], dt: { in: [], out: [], center: [], storeWidth: 1.1, storeDepth: 1.3 } }, { id: '28GxDYUqDwZc2WsOgMU2wi', t: 'gstore', v: true, @@ -271,7 +271,7 @@ export default { dt: { in: [], out: [], center: ['0aJ81sOKqm9FYo60AIQmMG'] } }, { id: 'ptr1', - t: 'ptr', + t: 'cl2', v: true, tf: [[5.65, 0.1, 2.13], [0, -90, 0], [1, 1, 1]], dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 } @@ -293,54 +293,68 @@ export default { v: true, tf: [[5.655, 0.1, 8.75], [0, 0, 0], [1, 1, 1]], dt: { in: [], out: [], center: [], chargerWidth: 0.53, chargerDepth: 0.275, clxHeight: 0.3 } - }, { - id: 'pallet1122', - t: 'pallet', - v: true, - tf: [[1.5, 0.1, 0.63], [0, 0, 0], [1.2, 0.15, 1]], - dt: { in: [], out: [], center: [], } - }, { + }, + { + id: 'pallet1122', // 托盘唯一编码 + t: 'pallet', // 托盘类型 + v: true, // 可见 + tf: [[1.5, 0.1, 0.63], [0, 0, 0], [1.2, 0.15, 1]], // 变换矩阵 + dt: { + in: [], out: [], center: [], + storeAt: { // 存储位置 + item: '6Vu3dX1V7Si0ISWIiCkoEh', // 库存容器ID + bay: 0, // 列 + level: 0, // 层 + cell: 0 // 格 + } + } + }, + { id: 'pallet1123', t: 'pallet', v: true, tf: [[7.1, 0.1, 2.865], [0, 0, 0], [1.2, 0.15, 1]], - dt: { in: [], out: [], center: [], } - }, { + dt: { + in: [], out: [], center: [], + storeAt: { item: '38TYyVWMGLf8OogQMIiSOz', bay: 0, level: 0, cell: 0 } + } + }, + { id: 'pallet1124', t: 'pallet', v: true, tf: [[4.196, 1.65, 3.95], [0, 0, 0], [1.2, 0.15, 1]], - dt: { in: [], out: [], center: [], } + dt: { in: [], out: [], center: [] } }, { id: 'pallet1125', t: 'pallet', v: true, tf: [[4.196, 1.65, 5.225], [0, 0, 0], [1.2, 0.15, 1]], - dt: { in: [], out: [], center: [], } + dt: { in: [], out: [], center: [] } }, { id: 'pallet1126', t: 'pallet', v: true, tf: [[4.196, 1.65, 7.775], [0, 0, 0], [1.2, 0.15, 1]], - dt: { in: [], out: [], center: [], } - },{ + dt: { in: [], out: [], center: [] } + }, { id: 'pallet1127', t: 'pallet', v: true, tf: [[4.196, 3.05, 3.95], [0, 0, 0], [1.2, 0.15, 1]], - dt: { in: [], out: [], center: [], } + dt: { in: [], out: [], center: [] } }, { id: 'pallet1128', t: 'pallet', v: true, tf: [[4.196, 3.05, 5.225], [0, 0, 0], [1.2, 0.15, 1]], - dt: { in: [], out: [], center: [], } + dt: { in: [], out: [], center: [] } }, { id: 'pallet1129', t: 'pallet', v: true, tf: [[4.196, 3.05, 7.775], [0, 0, 0], [1.2, 0.15, 1]], - dt: { in: [], out: [], center: [], } + dt: { in: [], out: [], center: [] } }] }, { diff --git a/src/modules/cl2/Cl2Entity.ts b/src/modules/cl2/Cl2Entity.ts new file mode 100644 index 0000000..06f4dbb --- /dev/null +++ b/src/modules/cl2/Cl2Entity.ts @@ -0,0 +1,72 @@ +import * as THREE from 'three' +import BaseEntity from '@/core/base/BaseItemEntity.ts' +import type Viewport from '@/core/engine/Viewport.ts' +import gsap from 'gsap' +import { nextTick } from 'vue' + +/** + * CL2 机械臂实体类 + */ +export default class Cl2Entity extends BaseEntity { + constructor(viewport: Viewport, id: string) { + super(viewport, id) + } + + // 装 + addLoad(item: string): void { + this.taskQueue.add(this.createTask('LOAD', async () => { + // 实际业务中应包含装载逻辑 + this.isCarrying = true + + const arm = this.getArmObject() + + // 抬高20cm + this.isLoading = true + gsap.to(arm.position, { + y: arm.position.y + 0.2, + duration: 0.5, + ease: 'sine.inOut', + onComplete: () => { + this.isCarrying = true + this.isLoading = false + const mesh = this.pickupItem(item) + const ptrForkMesh = this.getArmObject() + mesh.position.set(0, 0.1, 0) + mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0) + ptrForkMesh.add(mesh) + } + }) + })) + } + + // 卸 + addUnload(itemId: string, rackId: string, bay?: number, level?: number, cell?: number): void { + this.taskQueue.add(this.createTask('UNLOAD', async () => { + const item = this.viewport.entityManager.findItemById(itemId) + this.isCarrying = false + this.isUnloading = false + this.dropItem(item, rackId, bay, level) + })) + } + + // 帮助方法:获取机械臂对象 + getArmObject(): THREE.Object3D | undefined { + const agv = this.object as THREE.Group + if (agv.children.length > 1) { + const pillar = agv.children[1] + if (pillar.children.length > 1) { + return pillar.children[1] + } + } + return undefined + } + + // 帮助方法:获取机械臂立柱 + getArmPillar(): THREE.Object3D | undefined { + const agv = this.object as THREE.Group + if (agv.children.length > 1) { + return agv.children[1] + } + return undefined + } +} diff --git a/src/modules/cl2/Cl2Interaction.ts b/src/modules/cl2/Cl2Interaction.ts new file mode 100644 index 0000000..39ac680 --- /dev/null +++ b/src/modules/cl2/Cl2Interaction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class PtrInteraction extends BaseInteraction { + + get isSinglePointMode(): boolean { + return true + } + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + createPointOfItem(item: ItemJson, point: THREE.Vector3): ItemJson { + item = super.createPointOfItem(item, point) + + // 创建一个地堆货架 + item.dt.palletWidth = 1 // 宽度 + item.dt.palletDepth = 1.2 // 深度 + return item + } +} diff --git a/src/modules/cl2/Cl2PropertySetter.ts b/src/modules/cl2/Cl2PropertySetter.ts new file mode 100644 index 0000000..4a5cfc9 --- /dev/null +++ b/src/modules/cl2/Cl2PropertySetter.ts @@ -0,0 +1,20 @@ +import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; +import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; + +const propertySetter: PropertySetter = { + flatten: { + fields: [ + ...basicFieldsSetter, + { + dataPath: 'dt.palletWidth', label: '托盘宽度', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.palletDepth', label: '托盘深度', input: 'InputNumber', + inputProps: {}, + }, + ], + }, +}; + +export default propertySetter; diff --git a/src/modules/cl2/Cl2Renderer.ts b/src/modules/cl2/Cl2Renderer.ts new file mode 100644 index 0000000..bde5c3f --- /dev/null +++ b/src/modules/cl2/Cl2Renderer.ts @@ -0,0 +1,312 @@ +import * as THREE from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import Constract from '@/core/Constract.ts' +import { CSG } from 'three-csg-ts' + +/** + * ptr侧叉渲染器 + */ +export default class PtrRenderer extends BaseRenderer { + static POINT_NAME = 'ptr' + + pointMaterial: THREE.Material + + /** + * 默认点的高度, 防止和地面重合 + */ + readonly defulePositionY: number = Constract.HEIGHT_WAY + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1, 1, 1) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + readonly defaultLineWidth: number = 0.15 + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + /** + * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 + */ + override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) { + super.afterCreateOrUpdatePoint(item, option, object) + + const point = object + // point.position.y = this.defulePositionY + // point.scale.set(_.sumBy(item.dt.bays, b=>b.bayWidth), this.defaultScale.y, item.dt.rackDepth) + point.rotation.set( + THREE.MathUtils.degToRad(item.tf[1][0]), + THREE.MathUtils.degToRad(item.tf[1][1]), + THREE.MathUtils.degToRad(item.tf[1][2]) + ) + } + + + createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { + throw new Error('not allow store line.') + } + + updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { + throw new Error('not allow store line.') + } + + + createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + // 创建平面几何体 + if (!item.dt.ptrWidth || !item.dt.ptrDepth) { + system.showErrorDialog('field ptrWidth / ptrDepth is null!') + return null + } + + const group = new THREE.Group() + group.name = PtrRenderer.POINT_NAME + + const ptrPedestalGeometry = this.createPtrPedestal(item, option) + const ptrPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}); + const ptrPedestalMesh = new THREE.Mesh(ptrPedestalGeometry, ptrPedestalMaterial); + ptrPedestalMesh.name = 'ptrPedestalMesh' + + const ptrPillarGeometry = this.createPtrPillar(item, option); + const ptrPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}); + const ptrPillarMesh = new THREE.Mesh(ptrPillarGeometry, ptrPillarMaterial); + ptrPillarMesh.name = 'ptrPillarMesh' + + const ptrForkGeometry = this.createPtrFork(item, option); + const ptrForkMaterial = new THREE.MeshPhongMaterial({color: 0xff111111}); + const ptrForkMesh = new THREE.Mesh(ptrForkGeometry, ptrForkMaterial); + ptrForkMesh.name = 'ptrForkMesh' + + group.add(ptrPedestalMesh) + const groupPillar = new THREE.Group() + groupPillar.add(ptrPillarMesh) + groupPillar.add(ptrForkMesh) + group.add(groupPillar) + + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + return group + } + + + + dispose() { + super.dispose() + this.pointMaterial?.dispose() + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + throw new Error('Ptr createPointBasic not allow!') + } + + // 创建ptr的底座 + createPtrPedestal(item: ItemJson, option?: RendererCudOption): THREE.BufferGeometry { + // 参数配置 + const radius = 0.8; // 圆半径 + const lineDist = 0.75; // 切割线距离圆心距离 + const segments = 64; // 圆的分段精度 + + // 计算切割线与圆的交点 + const intersectY = Math.sqrt(radius*radius - lineDist*lineDist); + const startAngle = Math.asin(intersectY/radius); + const endAngle = Math.acos(intersectY/radius); + + + const shape = new THREE.Shape(); + shape.moveTo(0, 0); // 起点在圆心 + shape.absarc(0, 0, 0.8, startAngle, endAngle, false); // 从0到π绘制半圆 + shape.absarc(0, 0, 0.8, startAngle + Math.PI/2, endAngle + Math.PI/2, false); + shape.absarc(0, 0, 0.8, startAngle + Math.PI, endAngle + Math.PI, false); + shape.absarc(0, 0, 0.8, startAngle + Math.PI/2 * 3, endAngle + Math.PI/2 * 3, false); + + shape.lineTo(-0.5, -intersectY) + shape.lineTo(-0.5, -intersectY + 0.17) + shape.lineTo(0.75, -intersectY + 0.17) + shape.lineTo(0.75, intersectY - 0.17) + shape.lineTo(-0.5, intersectY - 0.17) + shape.lineTo(-0.5, intersectY) + 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 curveD = new THREE.EllipseCurve( + 0, 0, radius, radius, 0, Math.PI * 2, false, 0 + ); + // 生成拉伸路径点 + const pointsD = curveD.getPoints(segments).map(p => + new THREE.Vector3(p.x, 0.20, p.y) + ); + // 3. 挤出成型 + const optionsD = { + steps: segments, + bevelEnabled: false, + // bevelSegments: 0.01, + extrudePath: new THREE.CatmullRomCurve3(pointsD) + }; + + 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 + } + + // 创建ptr的立柱 + createPtrPillar(item: ItemJson, option?: RendererCudOption): THREE.BufferGeometry { + // 606.5 + const width = 0.245 + const depth = 0.716 + const dd = 0.05 + const shape = new THREE.Shape(); + shape.moveTo(-0.728, -0.358 + dd); + shape.lineTo(-0.728 + dd, -0.358); + shape.lineTo(-0.728 + width - dd, -0.358); + shape.lineTo(-0.728 + width, -0.358 + dd); + + shape.lineTo(-0.728 + width, -0.28); + shape.lineTo(-0.728 + width - 0.08, -0.28); + shape.lineTo(-0.728 + width - 0.08, 0.28); + shape.lineTo(-0.728 + width, 0.28); + + shape.lineTo(-0.728 + width, -0.358 + depth - dd); + shape.lineTo(-0.728 + width - dd, -0.358 + depth); + shape.lineTo(-0.728 + dd, -0.358 + depth); + shape.lineTo(-0.728, -0.358 + depth - dd); + // shape.lineTo(-0.728, -0.358 + dd); + // shape.closePath() + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.22, 0), new THREE.Vector3(0, 1.872, 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); + return geometry + + } + + // 创建ptr的立柱 + createPtrFork(item: ItemJson, option?: RendererCudOption): THREE.BufferGeometry { + // 606.5 + const width = 0.245 + const depth = 0.716 + const dd = 0.05 + const fd = 0.03 + const shape = new THREE.Shape(); + + shape.moveTo(-0.728 + width - 0.08, -0.28); + shape.lineTo(-0.728 + width + 0.02, -0.28); + shape.lineTo(-0.728 + width + 0.02, -0.27); + shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.27); + shape.lineTo(-0.728 + width + 0.02 + 1.130, -0.27 + fd); + shape.lineTo(-0.728 + width + 0.02 + 1.130, -0.12 - fd); + shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.12); + shape.lineTo(-0.728 + width + 0.02, -0.12); + shape.lineTo(-0.728 + width + 0.02, 0.12); + shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.12); + shape.lineTo(-0.728 + width + 0.02 + 1.130, 0.12 + fd); + shape.lineTo(-0.728 + width + 0.02 + 1.130, 0.27 - fd); + shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.27); + shape.lineTo(-0.728 + width + 0.02, 0.27); + shape.lineTo(-0.728 + width + 0.02, 0.28); + shape.lineTo(-0.728 + width - 0.08, 0.28); + + shape.closePath() + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.482, 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.728 + width + 0.02, -0.3); + shapeD.lineTo(-0.728 + width + 0.02 + 1.2, -0.3); + shapeD.lineTo(-0.728 + width + 0.02 + 1.2, 0.3); + shapeD.lineTo(-0.728 + width + 0.02, 0.3); + shape.closePath() + + // 拉伸轨迹线 + const curveD = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.07, 0), new THREE.Vector3(0, 0.482, 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 + } + +} diff --git a/src/modules/cl2/index.ts b/src/modules/cl2/index.ts new file mode 100644 index 0000000..5619f2e --- /dev/null +++ b/src/modules/cl2/index.ts @@ -0,0 +1,15 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import Cl2Renderer from './Cl2Renderer.ts' +import Cl2Entity from './Cl2Entity.ts' +import Cl2Interaction from './Cl2Interaction.ts' +import propertySetter from "./Cl2PropertySetter.ts"; + +export const ITEM_TYPE_NAME = 'cl2' + +export default defineModule({ + name: ITEM_TYPE_NAME, + renderer: new Cl2Renderer(ITEM_TYPE_NAME), + interaction: new Cl2Interaction(ITEM_TYPE_NAME), + setter: propertySetter, + entity: Cl2Entity, +}) diff --git a/src/modules/gstore/GstoreRenderer.ts b/src/modules/gstore/GstoreRenderer.ts index 606c60d..82a1fda 100644 --- a/src/modules/gstore/GstoreRenderer.ts +++ b/src/modules/gstore/GstoreRenderer.ts @@ -4,6 +4,7 @@ import Constract from '@/core/Constract.ts' import { type Object3DLike } from '@/types/ModelTypes.ts' import { getMatrixFromTf } from '@/core/ModelUtils.ts' import InstanceMeshManager from '@/core/manager/InstanceMeshManager.ts' +import type Viewport from '@/core/engine/Viewport.ts' /** * 地堆货位渲染器 @@ -163,4 +164,26 @@ export default class GstoreRenderer extends BaseRenderer { }) } + /** + * 获取物品存放位 + * 获取物品存储到地堆货位中,返回可存放的位置和角度一个 Position 和 Rotation + */ + getStorePlacement(viewport: Viewport, item: ItemJson): { position: THREE.Vector3, rotation: THREE.Quaternion } { + if (!item.dt?.storeAt?.item) { + // 没有定义存储位置,返回空对象 + //@ts-ignore + return {} + } + + // 将这个物品添加到库存中 + const me = viewport.entityManager.findItemById(item.dt.storeAt.item) + const position = new THREE.Vector3(...me.tf[0]) + const rotation = new THREE.Quaternion().setFromEuler(new THREE.Euler( + THREE.MathUtils.degToRad(me.tf[1][0]), + THREE.MathUtils.degToRad(me.tf[1][1]), + THREE.MathUtils.degToRad(me.tf[1][2]), + 'XYZ' + )) + return { position, rotation } + } } diff --git a/src/modules/ptr/PtrEntity.ts b/src/modules/ptr/PtrEntity.ts deleted file mode 100644 index 468396a..0000000 --- a/src/modules/ptr/PtrEntity.ts +++ /dev/null @@ -1,5 +0,0 @@ -import BaseEntity from '@/core/base/BaseItemEntity.ts' - -export default class PtrEntity extends BaseEntity { - -} diff --git a/src/modules/ptr/PtrInteraction.ts b/src/modules/ptr/PtrInteraction.ts deleted file mode 100644 index 39ac680..0000000 --- a/src/modules/ptr/PtrInteraction.ts +++ /dev/null @@ -1,22 +0,0 @@ -import BaseInteraction from '@/core/base/BaseInteraction.ts' -import * as THREE from 'three' - -export default class PtrInteraction extends BaseInteraction { - - get isSinglePointMode(): boolean { - return true - } - - constructor(itemTypeName: string) { - super(itemTypeName) - } - - createPointOfItem(item: ItemJson, point: THREE.Vector3): ItemJson { - item = super.createPointOfItem(item, point) - - // 创建一个地堆货架 - item.dt.palletWidth = 1 // 宽度 - item.dt.palletDepth = 1.2 // 深度 - return item - } -} diff --git a/src/modules/ptr/PtrPropertySetter.ts b/src/modules/ptr/PtrPropertySetter.ts deleted file mode 100644 index 4a5cfc9..0000000 --- a/src/modules/ptr/PtrPropertySetter.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; -import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; - -const propertySetter: PropertySetter = { - flatten: { - fields: [ - ...basicFieldsSetter, - { - dataPath: 'dt.palletWidth', label: '托盘宽度', input: 'InputNumber', - inputProps: {}, - }, - { - dataPath: 'dt.palletDepth', label: '托盘深度', input: 'InputNumber', - inputProps: {}, - }, - ], - }, -}; - -export default propertySetter; diff --git a/src/modules/ptr/PtrRenderer.ts b/src/modules/ptr/PtrRenderer.ts deleted file mode 100644 index 25e3eb4..0000000 --- a/src/modules/ptr/PtrRenderer.ts +++ /dev/null @@ -1,315 +0,0 @@ -import * as THREE from 'three' -import BaseRenderer from '@/core/base/BaseRenderer.ts' -import { Line2 } from 'three/examples/jsm/lines/Line2.js' -import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js' -import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js' -import { decimalSumBy } from '@/core/ModelUtils' -import Constract from '@/core/Constract.ts' -import ptrUrl from '@/assets/images/ptr/ptr.png' -import { CSG } from 'three-csg-ts'; -import {BufferGeometry} from "three"; - -/** - * ptr侧叉渲染器 - */ -export default class PtrRenderer extends BaseRenderer { - static POINT_NAME = 'ptr' - - pointMaterial: THREE.Material - - /** - * 默认点的高度, 防止和地面重合 - */ - readonly defulePositionY: number = Constract.HEIGHT_WAY - readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1, 1, 1) - readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) - readonly defaultLineWidth: number = 0.15 - - constructor(itemTypeName: string) { - super(itemTypeName) - } - - /** - * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 - */ - override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) { - super.afterCreateOrUpdatePoint(item, option, object) - - const point = object - // point.position.y = this.defulePositionY - // point.scale.set(_.sumBy(item.dt.bays, b=>b.bayWidth), this.defaultScale.y, item.dt.rackDepth) - point.rotation.set( - THREE.MathUtils.degToRad(item.tf[1][0]), - THREE.MathUtils.degToRad(item.tf[1][1]), - THREE.MathUtils.degToRad(item.tf[1][2]) - ) - } - - - createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { - throw new Error('not allow store line.') - } - - updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { - throw new Error('not allow store line.') - } - - - createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - // 创建平面几何体 - if (!item.dt.ptrWidth || !item.dt.ptrDepth) { - system.showErrorDialog('field ptrWidth / ptrDepth is null!') - return null - } - - const group = new THREE.Group() - group.name = PtrRenderer.POINT_NAME - - const ptrPedestalGeometry = this.createPtrPedestal(item, option) - const ptrPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}); - const ptrPedestalMesh = new THREE.Mesh(ptrPedestalGeometry, ptrPedestalMaterial); - - const ptrPillarGeometry = this.createPtrPillar(item, option); - const ptrPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}); - const ptrPillarMesh = new THREE.Mesh(ptrPillarGeometry, ptrPillarMaterial); - - const ptrForkGeometry = this.createPtrFork(item, option); - const ptrForkMaterial = new THREE.MeshPhongMaterial({color: 0xff111111}); - const ptrForkMesh = new THREE.Mesh(ptrForkGeometry, ptrForkMaterial); - - group.add(ptrPedestalMesh) - const groupPillar = new THREE.Group() - groupPillar.add(ptrPillarMesh) - groupPillar.add(ptrForkMesh) - group.add(groupPillar) - - // 设置位置 - group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) - return group - } - - - - dispose() { - super.dispose() - this.pointMaterial?.dispose() - } - - createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - throw new Error('Ptr createPointBasic not allow!') - } - - // 创建ptr的底座 - createPtrPedestal(item: ItemJson, option?: RendererCudOption): THREE.BufferGeometry { - // 参数配置 - const radius = 0.8; // 圆半径 - const lineDist = 0.75; // 切割线距离圆心距离 - const segments = 64; // 圆的分段精度 - - // 计算切割线与圆的交点 - const intersectY = Math.sqrt(radius*radius - lineDist*lineDist); - const startAngle = Math.asin(intersectY/radius); - const endAngle = Math.acos(intersectY/radius); - - - const shape = new THREE.Shape(); - shape.moveTo(0, 0); // 起点在圆心 - shape.absarc(0, 0, 0.8, startAngle, endAngle, false); // 从0到π绘制半圆 - shape.absarc(0, 0, 0.8, startAngle + Math.PI/2, endAngle + Math.PI/2, false); - shape.absarc(0, 0, 0.8, startAngle + Math.PI, endAngle + Math.PI, false); - shape.absarc(0, 0, 0.8, startAngle + Math.PI/2 * 3, endAngle + Math.PI/2 * 3, false); - - shape.lineTo(-0.5, -intersectY) - shape.lineTo(-0.5, -intersectY + 0.17) - shape.lineTo(0.75, -intersectY + 0.17) - shape.lineTo(0.75, intersectY - 0.17) - shape.lineTo(-0.5, intersectY - 0.17) - shape.lineTo(-0.5, intersectY) - 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 curveD = new THREE.EllipseCurve( - 0, 0, radius, radius, 0, Math.PI * 2, false, 0 - ); - // 生成拉伸路径点 - const pointsD = curveD.getPoints(segments).map(p => - new THREE.Vector3(p.x, 0.20, p.y) - ); - // 3. 挤出成型 - const optionsD = { - steps: segments, - bevelEnabled: false, - // bevelSegments: 0.01, - extrudePath: new THREE.CatmullRomCurve3(pointsD) - }; - - 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 - } - - // 创建ptr的立柱 - createPtrPillar(item: ItemJson, option?: RendererCudOption): THREE.BufferGeometry { - // 606.5 - const width = 0.245 - const depth = 0.716 - const dd = 0.05 - const shape = new THREE.Shape(); - shape.moveTo(-0.728, -0.358 + dd); - shape.lineTo(-0.728 + dd, -0.358); - shape.lineTo(-0.728 + width - dd, -0.358); - shape.lineTo(-0.728 + width, -0.358 + dd); - - shape.lineTo(-0.728 + width, -0.28); - shape.lineTo(-0.728 + width - 0.08, -0.28); - shape.lineTo(-0.728 + width - 0.08, 0.28); - shape.lineTo(-0.728 + width, 0.28); - - shape.lineTo(-0.728 + width, -0.358 + depth - dd); - shape.lineTo(-0.728 + width - dd, -0.358 + depth); - shape.lineTo(-0.728 + dd, -0.358 + depth); - shape.lineTo(-0.728, -0.358 + depth - dd); - // shape.lineTo(-0.728, -0.358 + dd); - // shape.closePath() - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.22, 0), new THREE.Vector3(0, 1.872, 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); - return geometry - - } - - // 创建ptr的立柱 - createPtrFork(item: ItemJson, option?: RendererCudOption): THREE.BufferGeometry { - // 606.5 - const width = 0.245 - const depth = 0.716 - const dd = 0.05 - const fd = 0.03 - const shape = new THREE.Shape(); - - shape.moveTo(-0.728 + width - 0.08, -0.28); - shape.lineTo(-0.728 + width + 0.02, -0.28); - shape.lineTo(-0.728 + width + 0.02, -0.27); - shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.27); - shape.lineTo(-0.728 + width + 0.02 + 1.130, -0.27 + fd); - shape.lineTo(-0.728 + width + 0.02 + 1.130, -0.12 - fd); - shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.12); - shape.lineTo(-0.728 + width + 0.02, -0.12); - shape.lineTo(-0.728 + width + 0.02, 0.12); - shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.12); - shape.lineTo(-0.728 + width + 0.02 + 1.130, 0.12 + fd); - shape.lineTo(-0.728 + width + 0.02 + 1.130, 0.27 - fd); - shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.27); - shape.lineTo(-0.728 + width + 0.02, 0.27); - shape.lineTo(-0.728 + width + 0.02, 0.28); - shape.lineTo(-0.728 + width - 0.08, 0.28); - - shape.closePath() - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.482, 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.728 + width + 0.02, -0.3); - shapeD.lineTo(-0.728 + width + 0.02 + 1.2, -0.3); - shapeD.lineTo(-0.728 + width + 0.02 + 1.2, 0.3); - shapeD.lineTo(-0.728 + width + 0.02, 0.3); - shape.closePath() - - // 拉伸轨迹线 - const curveD = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.07, 0), new THREE.Vector3(0, 0.482, 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 - } - -} diff --git a/src/modules/ptr/index.ts b/src/modules/ptr/index.ts deleted file mode 100644 index 6679938..0000000 --- a/src/modules/ptr/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { defineModule } from '@/core/manager/ModuleManager.ts' -import PtrRenderer from './PtrRenderer.ts' -import PtrEntity from './PtrEntity.ts' -import PtrInteraction from './PtrInteraction.ts' -import propertySetter from "@/modules/ptr/PtrPropertySetter.ts"; - -export const ITEM_TYPE_NAME = 'ptr' - -export default defineModule({ - name: ITEM_TYPE_NAME, - renderer: new PtrRenderer(ITEM_TYPE_NAME), - interaction: new PtrInteraction(ITEM_TYPE_NAME), - setter: propertySetter, - entity: PtrEntity, -}) diff --git a/src/types/ScriptSupport.d.ts b/src/types/ScriptSupport.d.ts index a811cb5..5d85824 100644 --- a/src/types/ScriptSupport.d.ts +++ b/src/types/ScriptSupport.d.ts @@ -21,6 +21,16 @@ declare interface Model { createTask(agv: object): TaskManagerIF /** + * 根据ID获取 CL2 叉车控制器 + */ + getCl2(id: string): Cl2If + + /** + * 根据ID获取 CLX 叉车控制器 + */ + getClx(id: string): ClxIf + + /** * 获取选中的对象 */ selectedObject: any diff --git a/src/types/model.d.ts b/src/types/model.d.ts index ce1a90a..986d724 100644 --- a/src/types/model.d.ts +++ b/src/types/model.d.ts @@ -39,7 +39,12 @@ interface InteractionCudOption { * 渲染器操作选项 */ interface RendererCudOption { - createFromInteraction: boolean + createFromInteraction?: boolean + isRuntime?: boolean + //THREE.Vector3 + position?: any + //THREE.Quaternion + rotation?: any } /** @@ -213,6 +218,16 @@ interface ItemJson extends ItemMetrix { protected?: boolean /** + * 物品所在的存储位置 + */ + storeAt?: { + item: string, // 货架(地堆货位)ID + bay?: number, // 货架(地堆货位)的列 + level?: number // 货架(地堆货位)的层 + cell?: number // 货架(地堆货位)的格 + } + + /** * 其他自定义数据, 可以存储任何数据 */ [key: string]: any @@ -266,3 +281,164 @@ interface UserData { [key: string]: any } + +/** + * 三维物体对象 + */ +type Object3DIF = any + +/** + * 被搬运物体 + */ +interface Loadable { + /** + * 拿起物体 + */ + pickupItem(item: string) + + /** + * 卸下物体 + */ + dropItem(item: ItemJson, storeItemId: string, bay?: number, level?: number, cell?: number): void +} + +/** + * 搬运能力 + */ +interface Carry { + + /** + * 是否正在搬运物体 + */ + isCarrying: boolean + + /** + * 是否正在装载 + */ + isLoading: boolean + + /** + * 是否正在卸载 + */ + isUnloading: boolean + + /** + * 装载 + */ + addLoad(item: string): void + + /** + * 卸载 + */ + addUnload(item: string, rack: string, bay?: number, level?: number): void +} + +/** + * 行走能力 + */ +interface Walk { + isWalking: boolean + + /** + * 行走到指定位置 + */ + addTravel(pos: Vector3IF | string): void + + /** + * 旋转到指定角度 + */ + addRotation(angle: number): void +} + +/** + * 叉臂能力 + */ +interface ForkArm { + isArmExtended: boolean + + /** + * 升出叉臂 + */ + addArmExtender(): void + + /** + * 收回叉臂 + */ + addArmRetractor(): void +} + +/** + * 升降塔能力 + */ +interface LiftingArm { + + isArmRaised: boolean + + /** + * 升起叉臂 + */ + addArmRaise(hight: number): void + + /** + * 降下叉臂 + */ + addArmLower(): void +} + +interface EntityIf { + id: string + + t: string + + item: ItemJson + + object: Object3DIF + + /** + * 取消所有任务 + */ + cancelTask(): void + + /** + * 暂停任务 + */ + taskPause(): void + + + /** + * 恢复任务 + */ + taskResume(): void + + /** + * 开始执行任务 + */ + taskStartRun(): void + + /** + * 是否已暂停 + */ + isPaused: boolean + + /** + * 当前任务数 + */ + taskCount: number + + /** + * 当前任务是否正在运行 + */ + taskIsRunning: boolean +} + +/** + * CL2 叉车接口 + */ +interface Cl2If extends EntityIf, Carry, Walk, ForkArm, LiftingArm { +} + +/** + * CLX 叉车接口 + */ +interface ClxIf extends EntityIf, Carry, Walk, ForkArm, LiftingArm { +}