From 1d9c97b159c316a8a4b9cc7f5e5f8126f50f502f Mon Sep 17 00:00:00 2001 From: yuliang <398780299@qq.com> Date: Fri, 25 Jul 2025 20:13:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=98=89=E5=85=B4=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/manager/WorldModel.ts | 4 +- src/core/script/ModelManager.ts | 5 + src/modules/amr/cc/cc5/CC53DGraphics.ts | 400 +++++++++++++++++++++++++++ src/modules/amr/cc/cc5/CC53dObject.ts | 305 ++++++++++++++++++++ src/modules/amr/cc/cc5/CC5Entity.ts | 11 + src/modules/amr/cc/cc5/CC5Interaction.ts | 22 ++ src/modules/amr/cc/cc5/CC5PropertySetter.ts | 24 ++ src/modules/amr/cc/cc5/CC5Renderer.ts | 119 ++++++++ src/modules/amr/cc/cc5/index.ts | 12 + src/modules/amr/fm600/FM6003DGraphics.ts | 198 +++++++++++++ src/modules/amr/fm600/FM6003dObject.ts | 157 +++++++++++ src/modules/amr/fm600/FM600Entity.ts | 120 ++++++++ src/modules/amr/fm600/FM600Interaction.ts | 22 ++ src/modules/amr/fm600/FM600PropertySetter.ts | 24 ++ src/modules/amr/fm600/FM600Renderer.ts | 122 ++++++++ src/modules/amr/fm600/index.ts | 12 + src/modules/amr/ptr/PtrObject.ts | 41 ++- src/types/Model.d.ts | 8 + 18 files changed, 1602 insertions(+), 4 deletions(-) create mode 100644 src/modules/amr/cc/cc5/CC53DGraphics.ts create mode 100644 src/modules/amr/cc/cc5/CC53dObject.ts create mode 100644 src/modules/amr/cc/cc5/CC5Entity.ts create mode 100644 src/modules/amr/cc/cc5/CC5Interaction.ts create mode 100644 src/modules/amr/cc/cc5/CC5PropertySetter.ts create mode 100644 src/modules/amr/cc/cc5/CC5Renderer.ts create mode 100644 src/modules/amr/cc/cc5/index.ts create mode 100644 src/modules/amr/fm600/FM6003DGraphics.ts create mode 100644 src/modules/amr/fm600/FM6003dObject.ts create mode 100644 src/modules/amr/fm600/FM600Entity.ts create mode 100644 src/modules/amr/fm600/FM600Interaction.ts create mode 100644 src/modules/amr/fm600/FM600PropertySetter.ts create mode 100644 src/modules/amr/fm600/FM600Renderer.ts create mode 100644 src/modules/amr/fm600/index.ts diff --git a/src/core/manager/WorldModel.ts b/src/core/manager/WorldModel.ts index 56044eb..aa6a42a 100644 --- a/src/core/manager/WorldModel.ts +++ b/src/core/manager/WorldModel.ts @@ -122,7 +122,9 @@ export default class WorldModel { import('../../modules/amr/ptr/cl2'), import('../../modules/amr/ptr/clx'), import('../../modules/charger'), - import('../../modules/bracket') + import('../../modules/bracket'), + import('../../modules/amr/fm600'), + import('../../modules/amr/cc/cc5') ]).then((configRes) => { console.log('世界模型初始化完成') diff --git a/src/core/script/ModelManager.ts b/src/core/script/ModelManager.ts index 078ae0e..e0e0895 100644 --- a/src/core/script/ModelManager.ts +++ b/src/core/script/ModelManager.ts @@ -7,6 +7,7 @@ import TaskManager from '../manager/TaskManager.ts' import Cl2Entity from '@/modules/amr/ptr/cl2/Cl2Entity.ts' // import ClxEntity from '@/modules/amr/ptr/clx/ClxEntity.ts' import { getRenderer } from '@/core/manager/ModuleManager.ts' +import FM600Entity from "@/modules/amr/fm600/FM600Entity"; export default class ModelManager implements IControls, Model { private viewport: Viewport @@ -20,6 +21,10 @@ export default class ModelManager implements IControls, Model { // return new ClxEntity(this.viewport, id) } + getFm600(id: string): Fm600If { + return new FM600Entity(this.viewport, id) + } + createTask(agv: object): TaskManager { if (TaskManager.taskMap.has(agv)) { return TaskManager.taskMap.get(agv) diff --git a/src/modules/amr/cc/cc5/CC53DGraphics.ts b/src/modules/amr/cc/cc5/CC53DGraphics.ts new file mode 100644 index 0000000..d6e6798 --- /dev/null +++ b/src/modules/amr/cc/cc5/CC53DGraphics.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 CC53DGraphics { + + // 创建cc5的底座 + static createCC5Pedestal(): 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 + } + + // 创建cc5的立柱 + static createCC5Pillar(): 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) + + } + + // 创建cc5的叉 + static createCC5Fork(): 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 + } + + // 创建cc5的铰链 + static createCC5Gemel(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) + } + + // 创建cc5叉的背板 + static createCC5ForkBasePlate(): 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, -1.5/*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 cc5PedestalGeometry: THREE.BufferGeometry = null + static cc5PillarGeometry: THREE.BufferGeometry = null + static cc5ForkGeometry: THREE.BufferGeometry = null + static cc5GemelGeometryL: THREE.BufferGeometry = null + static cc5GemelGeometryR: THREE.BufferGeometry = null + static cc5ForkBasePlateGeometry: THREE.BufferGeometry = null +} diff --git a/src/modules/amr/cc/cc5/CC53dObject.ts b/src/modules/amr/cc/cc5/CC53dObject.ts new file mode 100644 index 0000000..04c8ba3 --- /dev/null +++ b/src/modules/amr/cc/cc5/CC53dObject.ts @@ -0,0 +1,305 @@ +import * as THREE from 'three' +import {CSG} from 'three-csg-ts' +import gsap from 'gsap' +//@ts-ignore +import {mergeGeometries} from 'three/addons/utils/BufferGeometryUtils.js' +import CC53DGraphics from "@/modules/amr/cc/cc5/CC53DGraphics"; +import Viewport from "@/core/engine/Viewport"; +import PtrObject from "@/modules/amr/ptr/PtrObject"; +import {worldModel} from "@/core/manager/WorldModel"; + + +export default class CC53dObject extends PtrObject { + + override __rotationSpeed = (Math.PI / 10) + override __showForkSpeed: number = 0.15 + override __upForkSpeed: number = 0.15 + + private get upForkSpeed() { + return this.__upForkSpeed * (worldModel.state.runState.timeRate ?? 1) + } + private get showForkSpeed() { + return this.__showForkSpeed * (worldModel.state.runState.timeRate ?? 1) + } + private get rotationSpeed() { + return this.__rotationSpeed * (worldModel.state.runState.timeRate ?? 1) + } + + constructor(item: ItemJson, viewport: Viewport, option?: RendererCudOption) { + super(item, viewport) + + if (!CC53DGraphics.cc5PedestalGeometry) { + CC53DGraphics.cc5PedestalGeometry = CC53DGraphics.createCC5Pedestal() + } + const cc5PedestalGeometry = CC53DGraphics.cc5PedestalGeometry + const cc5PedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}) + const cc5PedestalMesh = new THREE.Mesh(cc5PedestalGeometry, cc5PedestalMaterial) + + if (!CC53DGraphics.cc5PillarGeometry) { + CC53DGraphics.cc5PillarGeometry = CC53DGraphics.createCC5Pillar() + } + const cc5PillarGeometry = CC53DGraphics.cc5PillarGeometry + const cc5PillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}) + const cc5PillarMesh = new THREE.Mesh(cc5PillarGeometry, cc5PillarMaterial) + + if (!CC53DGraphics.cc5ForkGeometry) { + CC53DGraphics.cc5ForkGeometry = CC53DGraphics.createCC5Fork() + } + const cc5ForkGeometry = CC53DGraphics.cc5ForkGeometry + const cc5ForkMaterial = new THREE.MeshPhongMaterial({color: 0xff444444}) + const cc5ForkMesh = new THREE.Mesh(cc5ForkGeometry, cc5ForkMaterial) + cc5ForkMesh.name = 'cc5Fork' + + if (!CC53DGraphics.cc5GemelGeometryL) { + CC53DGraphics.cc5GemelGeometryL = CC53DGraphics.createCC5Gemel(true) + } + const cc5GemelGeometryL = CC53DGraphics.cc5GemelGeometryL + + if (!CC53DGraphics.cc5GemelGeometryR) { + CC53DGraphics.cc5GemelGeometryR = CC53DGraphics.createCC5Gemel(false) + } + const cc5GemelGeometryR = CC53DGraphics.cc5GemelGeometryR + + const cc5GemelMaterial = new THREE.MeshPhongMaterial({color: 0xff555555}) + const cc5GemelMeshL1 = new THREE.Mesh(cc5GemelGeometryL, cc5GemelMaterial) + cc5GemelMeshL1.name = 'cc5GemelMeshL1' + const cc5GemelMeshL2 = new THREE.Mesh(cc5GemelGeometryL, cc5GemelMaterial) + cc5GemelMeshL2.name = 'cc5GemelMeshL2' + const cc5GemelMeshR1 = new THREE.Mesh(cc5GemelGeometryR, cc5GemelMaterial) + cc5GemelMeshR1.name = 'cc5GemelMeshR1' + const cc5GemelMeshR2 = new THREE.Mesh(cc5GemelGeometryR, cc5GemelMaterial) + cc5GemelMeshR2.name = 'cc5GemelMeshR2' + + if (!CC53DGraphics.cc5ForkBasePlateGeometry) { + CC53DGraphics.cc5ForkBasePlateGeometry = CC53DGraphics.createCC5ForkBasePlate() + } + const cc5ForkBasePlateGeometry = CC53DGraphics.cc5ForkBasePlateGeometry + const cc5ForkBasePlateMesh = new THREE.Mesh(cc5ForkBasePlateGeometry, cc5GemelMaterial) + cc5ForkBasePlateMesh.name = 'cc5ForkBasePlateMesh' + + const d = 0 + + this.add(cc5PedestalMesh) + this.add(cc5PillarMesh) + this.add(cc5ForkMesh) + + + const ac = Math.asin(d / (2 * 0.8)) + + + cc5GemelMeshL1.position.z = 0.5 - d / 4 + cc5GemelMeshL1.position.y = 0.72 + cc5GemelMeshL1.rotation.x = -ac + + cc5GemelMeshL2.position.z = 0.5 - d / 4 * 3 + cc5GemelMeshL2.position.y = 0.72 + cc5GemelMeshL2.rotation.x = -ac + + cc5GemelMeshR1.position.z = 0.5 - d / 4 + cc5GemelMeshR1.position.y = 0.72 + cc5GemelMeshR1.rotation.x = ac + + cc5GemelMeshR2.position.z = 0.5 - d / 4 * 3 + cc5GemelMeshR2.position.y = 0.72 + cc5GemelMeshR2.rotation.x = ac + + this.add(cc5GemelMeshL1) + this.add(cc5GemelMeshL2) + this.add(cc5GemelMeshR1) + this.add(cc5GemelMeshR2) + this.add(cc5ForkBasePlateMesh) + } + + override animationShowFork(z: number): Promise { + const cc5Fork = this.getObjectByName('cc5Fork') + const cc5GemelMeshL1 = this.getObjectByName('cc5GemelMeshL1') + const cc5GemelMeshL2 = this.getObjectByName('cc5GemelMeshL2') + const cc5GemelMeshR1 = this.getObjectByName('cc5GemelMeshR1') + const cc5GemelMeshR2 = this.getObjectByName('cc5GemelMeshR2') + const cc5ForkBasePlateMesh = this.getObjectByName('cc5ForkBasePlateMesh') + + const fy = cc5Fork.position.y + + const time = Math.abs((cc5Fork.position.z - z) / this.upForkSpeed) + + const ac = Math.asin(z / (2 * 0.8)) + + // cc5GemelMeshL1.position.z = 0.5 - z/4 + // cc5GemelMeshL1.position.y = fy + 0.72 + // cc5GemelMeshL1.rotation.x = -ac + gsap.to(cc5GemelMeshL1.position, { + z: 0.5 - z / 4, + y: fy + 0.72, + duration: time, + repeat: 0, + ease: 'power1.inOut' + }) + gsap.to(cc5GemelMeshL1.rotation, { + x: -ac, + duration: time, + repeat: 0, + ease: 'power1.inOut' + }) + // cc5GemelMeshL2.position.z = 0.5 - z/4 * 3 + // cc5GemelMeshL2.position.y = fy + 0.72 + // cc5GemelMeshL2.rotation.x = -ac + gsap.to(cc5GemelMeshL2.position, { + z: 0.5 - z / 4 * 3, + y: fy + 0.72, + duration: time, + repeat: 0, + ease: 'power1.inOut' + }) + gsap.to(cc5GemelMeshL2.rotation, { + x: -ac, + duration: time, + repeat: 0, + ease: 'power1.inOut' + }) + + // cc5GemelMeshR1.position.z = 0.5 - z/4 + // cc5GemelMeshR1.position.y = fy + 0.72 + // cc5GemelMeshR1.rotation.x = ac + gsap.to(cc5GemelMeshR1.position, { + z: 0.5 - z / 4, + y: fy + 0.72, + duration: time, + repeat: 0, + ease: 'power1.inOut' + }) + gsap.to(cc5GemelMeshR1.rotation, { + x: ac, + duration: time, + repeat: 0, + ease: 'power1.inOut' + }) + + // cc5GemelMeshR2.position.z = 0.5 - z/4 * 3 + // cc5GemelMeshR2.position.y = fy + 0.72 + // cc5GemelMeshR2.rotation.x = ac + gsap.to(cc5GemelMeshR2.position, { + z: 0.5 - z / 4 * 3, + y: fy + 0.72, + duration: time, + repeat: 0, + ease: 'power1.inOut' + }) + gsap.to(cc5GemelMeshR2.rotation, { + x: ac, + duration: time, + repeat: 0, + ease: 'power1.inOut' + }) + + gsap.to(cc5ForkBasePlateMesh.position, { + y: fy, + duration: time, + repeat: 0, + ease: 'power1.inOut' + }) + + return new Promise(resolve => { + this.actionAnimation = gsap.to(cc5Fork.position, { + z: -z, + duration: time, + repeat: 0, + ease: 'power1.inOut', + onComplete: ()=>{ + setTimeout(() => { + resolve() + }, 1000 / (worldModel.state.runState.timeRate ?? 1)) + }, + }) + }) + + } + + override animationHideFork(): Promise { + return this.animationShowFork(0) + } + + override animationUpFork(y: number): Promise { + const cc5Fork = this.getObjectByName('cc5Fork') + const cc5GemelMeshL1 = this.getObjectByName('cc5GemelMeshL1') + const cc5GemelMeshL2 = this.getObjectByName('cc5GemelMeshL2') + const cc5GemelMeshR1 = this.getObjectByName('cc5GemelMeshR1') + const cc5GemelMeshR2 = this.getObjectByName('cc5GemelMeshR2') + const cc5ForkBasePlateMesh = this.getObjectByName('cc5ForkBasePlateMesh') + + + const time = Math.abs((cc5Fork.position.y - y) / this.upForkSpeed) + + gsap.to(cc5GemelMeshL1.position, { + y: y + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(cc5GemelMeshL2.position, { + y: y + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(cc5GemelMeshR1.position, { + y: y + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(cc5GemelMeshR2.position, { + y: y + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(cc5ForkBasePlateMesh.position, { + y: y, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + + return new Promise(resolve => { + const bh = 0.22 + const children = cc5Fork.children + + this.actionAnimation = gsap.to(cc5Fork.position, { + y: y, + duration: time, + repeat: 0, + ease: 'sine.inOut', + onComplete: ()=>{ + setTimeout(() => { + resolve() + }, 1000 / (worldModel.state.runState.timeRate ?? 1)) + }, + onUpdate: function() { + const a = this.targets()[0] + if (a.y < bh) { + if (a.z > -1) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + child.position.y = bh - a.y + } + } else if (a.y < 0 ) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + child.position.y = 0 - a.y + } + } + } + } + }) + }) + } + + override animationDownFork(): Promise { + return this.animationUpFork(0) + } + + override getArmObject(): THREE.Object3D | undefined { + const cc5Fork = this.getObjectByName('cc5Fork') + return cc5Fork + } +} diff --git a/src/modules/amr/cc/cc5/CC5Entity.ts b/src/modules/amr/cc/cc5/CC5Entity.ts new file mode 100644 index 0000000..09d39f5 --- /dev/null +++ b/src/modules/amr/cc/cc5/CC5Entity.ts @@ -0,0 +1,11 @@ +import BaseEntity from '@/core/base/BaseItemEntity.ts' +import type Viewport from '@/core/engine/Viewport.ts' +import * as THREE from 'three' +import gsap from 'gsap' +import CC53dObject from './CC53dObject.ts' + +export default class CC5Entity extends BaseEntity { + constructor(viewport: Viewport, id: string) { + super(viewport, id) + } +} diff --git a/src/modules/amr/cc/cc5/CC5Interaction.ts b/src/modules/amr/cc/cc5/CC5Interaction.ts new file mode 100644 index 0000000..29482e4 --- /dev/null +++ b/src/modules/amr/cc/cc5/CC5Interaction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class CC5Interaction 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/amr/cc/cc5/CC5PropertySetter.ts b/src/modules/amr/cc/cc5/CC5PropertySetter.ts new file mode 100644 index 0000000..9a69a24 --- /dev/null +++ b/src/modules/amr/cc/cc5/CC5PropertySetter.ts @@ -0,0 +1,24 @@ +import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; +import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; + +const propertySetter: PropertySetter = { + flatten: { + fields: [ + ...basicFieldsSetter, + { + dataPath: 'dt.clxWidth', label: 'CC5宽度', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.clxDepth', label: 'CC5深度', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'state', label: 'CC5控制', input: 'PtrController', + inputProps: {}, + }, + ], + }, +}; + +export default propertySetter; diff --git a/src/modules/amr/cc/cc5/CC5Renderer.ts b/src/modules/amr/cc/cc5/CC5Renderer.ts new file mode 100644 index 0000000..1978bbe --- /dev/null +++ b/src/modules/amr/cc/cc5/CC5Renderer.ts @@ -0,0 +1,119 @@ +import * as THREE from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import Constract from '@/core/Constract.ts' +import CC53dObject from './CC53dObject' +import type { Object3DLike } from '@/types/ModelTypes.ts' +import Viewport from "@/core/engine/Viewport"; + +/** + * clx渲染器 + */ +export default class CC5Renderer extends BaseRenderer { + static POINT_NAME = 'cc5' + + pointMaterial: THREE.Material + + /** + * 默认点的高度, 防止和地面重合 + */ + readonly defulePositionY: number = Constract.HEIGHT_WAY + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1.65, 3.393, 1.65) + 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 { + // 创建平面几何体 + const group = new CC53dObject(item, this.tempViewport, option) + group.name = CC5Renderer.POINT_NAME + + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + return group + } + + /** + * 如果某物品要放到 Cl2 上 返回可存放的位置和角度一个 Position 和 Rotation + */ + getStorePlacement(storeItem: ItemJson, bay = 0, level = 0, cell = 0) + : { + position: [number, number, number], rotation: [number, number, number], + getParentObject3D?: (viewport: Viewport, parent: THREE.Object3D) => THREE.Object3D + } { + + return { + position: [0, 0.2, -0.15], + rotation: [0, 90, 0], + getParentObject3D: this.getArmObject.bind(this) + } + } + getArmObject(viewport: Viewport, item: ItemJson): THREE.Object3D { + // 获取机械臂对象 + const object = viewport.entityManager.findObjectById(item.dt.storeAt?.item) + if (!object) { + console.warn('PtrRenderer: getArmObject failed, not found Cl2:', item.dt.storeAt?.item) + return + } + + const agv = object as THREE.Group + const clxFork = agv.getObjectByName('clxFork') + return clxFork + } + + updatePoint(item: ItemJson, object: Object3DLike, option?: RendererCudOption): Object3DLike { + const group: THREE.Group = object as THREE.Group + + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + group.rotation.set( + THREE.MathUtils.degToRad(item.tf[1][0]), + THREE.MathUtils.degToRad(item.tf[1][1]), + THREE.MathUtils.degToRad(item.tf[1][2]) + ) + + // 禁止缩放, + item.tf[2][0] = this.defaultScale.x + item.tf[2][1] = this.defaultScale.y + item.tf[2][2] = this.defaultScale.z + + return group + } + + dispose() { + super.dispose() + this.pointMaterial?.dispose() + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + throw new Error('Clx createPointBasic not allow!') + } + +} diff --git a/src/modules/amr/cc/cc5/index.ts b/src/modules/amr/cc/cc5/index.ts new file mode 100644 index 0000000..9fb2983 --- /dev/null +++ b/src/modules/amr/cc/cc5/index.ts @@ -0,0 +1,12 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import CC5Renderer from './CC5Renderer.ts' +import CC5Interaction from './CC5Interaction.ts' +import propertySetter from './CC5PropertySetter.ts' + +export const ITEM_TYPE_NAME = 'cc5' + +export default defineModule(ITEM_TYPE_NAME, () => ({ + renderer: new CC5Renderer(ITEM_TYPE_NAME), + interaction: new CC5Interaction(ITEM_TYPE_NAME), + setter: propertySetter +})) diff --git a/src/modules/amr/fm600/FM6003DGraphics.ts b/src/modules/amr/fm600/FM6003DGraphics.ts new file mode 100644 index 0000000..a0657ee --- /dev/null +++ b/src/modules/amr/fm600/FM6003DGraphics.ts @@ -0,0 +1,198 @@ +import * as THREE from "three"; +import {CSG} from "three-csg-ts"; +//@ts-ignore +import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js' + +export default class FM6003DGraphics { + +// 创建ptr的底座 + public static createFm600Pedestal(): THREE.BufferGeometry { + // 参数配置 + const radius = 0.475 // 圆半径 + const lineDist = 0.325 // 切割线距离圆心距离 + const segments = 64 // 圆的分段精度 + + // 计算切割线与圆的交点 + const intersectY = Math.sqrt(radius * radius - lineDist * lineDist) + const startAngle = Math.asin(intersectY / radius) + const endAngle = Math.PI - startAngle //Math.acos(intersectY / radius) + + + const shape = new THREE.Shape() + shape.moveTo(0, 0) // 起点在圆心 + shape.absarc(0, 0, radius, startAngle, endAngle, false) // 从0到π绘制半圆 + // shape.lineTo(-lineDist, intersectY) + shape.absarc(0, 0, radius, startAngle + Math.PI, endAngle + Math.PI, false) + + + // shape.lineTo(lineDist, -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.closePath() // 闭合路径 + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.025, 0), new THREE.Vector3(0, 0.235, 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.035, -0.02) + shapeD.lineTo(0.035, 0.02) + shapeD.lineTo(-0.035, 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.200, 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 + } + +// 创建fm600的顶盘 + public static createFm600Plate(): THREE.BufferGeometry { + // 参数配置 + const radius = 0.425 // 圆半径 + const lineDist = 0.3 // 切割线距离圆心距离 + const segments = 64 // 圆的分段精度 + + // 计算切割线与圆的交点 + const intersectY = Math.sqrt(radius * radius - lineDist * lineDist) + const startAngle = Math.asin(intersectY / radius) + const endAngle = Math.PI - startAngle //Math.acos(intersectY / radius) + + + const shape = new THREE.Shape() + shape.moveTo(0, 0) // 起点在圆心 + shape.absarc(0, 0, radius, startAngle, endAngle, false) // 从0到π绘制半圆 + // shape.lineTo(-lineDist, intersectY) + shape.absarc(0, 0, radius, startAngle + Math.PI, endAngle + Math.PI, false) + shape.closePath() // 闭合路径 + + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.24, 0), new THREE.Vector3(0, 0.255, 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 holeShape = new THREE.Shape() + holeShape.moveTo(0, 0) + holeShape.absarc(0, 0, 0.1, 0, Math.PI * 2, false) // 从0到π绘制半圆 + // 拉伸轨迹线 + const holeCurve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.24, 0), new THREE.Vector3(0, 0.255, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + // 挤出几何图形 参数 + const holeOptions = { + steps: 1, + bevelEnabled: false, + extrudePath: holeCurve // 设置挤出轨迹 + } + + // 创建挤出几何体 + const geometryD = new THREE.ExtrudeGeometry(holeShape, holeOptions) + const meshD = new THREE.Mesh(geometryD, material) + meshD.updateMatrix() + // 布尔运算 + const result = CSG.subtract(mesh, meshD) + return result.geometry + } + + public static createFm600Header(): THREE.BufferGeometry { + + const radius = 0.475 // 圆半径 + const lineDist = 0.325 // 切割线距离圆心距离 + const segments = 64 // 圆的分段精度 + + // 计算切割线与圆的交点 + const intersectY = Math.sqrt(radius * radius - lineDist * lineDist) + const startAngle = Math.asin(intersectY / radius) + const endAngle = Math.PI - startAngle //Math.acos(intersectY / radius) + + const shapeH = new THREE.Shape() + shapeH.moveTo(-0.02, -0.02) + shapeH.lineTo(0.02, 0.02) + shapeH.lineTo(-0.02, 0.02) + shapeH.closePath() + + const curveH = new THREE.EllipseCurve( + 0, 0, radius-0.02, radius-0.02, Math.PI * 2 - startAngle + 0.1, startAngle - 0.1,false, 0 + ) + // 生成拉伸路径点 + const pointsH = curveH.getPoints(segments).map(p => + new THREE.Vector3(p.x, 0.210, p.y) + ) + const optionsH = { + steps: segments, + bevelEnabled: false, + // bevelSegments: 0.01, + extrudePath: new THREE.CatmullRomCurve3(pointsH) + } + const geometryH = new THREE.ExtrudeGeometry(shapeH, optionsH) + return geometryH + } + + + public static fm600PedestalGeometry: THREE.BufferGeometry = null + public static fm600PlateGeometry: THREE.BufferGeometry = null + public static fm600HeaderGeometry: THREE.BufferGeometry = null + +} diff --git a/src/modules/amr/fm600/FM6003dObject.ts b/src/modules/amr/fm600/FM6003dObject.ts new file mode 100644 index 0000000..b035155 --- /dev/null +++ b/src/modules/amr/fm600/FM6003dObject.ts @@ -0,0 +1,157 @@ +import * as THREE from 'three' +import {CSG} from 'three-csg-ts' +import gsap from 'gsap' +import mqtt from 'mqtt' +import {Euler} from 'three/src/math/Euler' +import FM600Entity from './FM600Entity' +import FM6003DGraphics from "./FM6003DGraphics" +import PtrObject from "@/modules/amr/ptr/PtrObject"; +import Viewport from "@/core/engine/Viewport"; +import {worldModel} from "@/core/manager/WorldModel"; + +export default class FM6003dObject extends PtrObject { + + private _fm600Entity: FM600Entity = null + + override __rotationSpeed = (Math.PI / 7) + override __showForkSpeed: number = 0.2 + override __upForkSpeed: number = 0.05 + + private get upForkSpeed() { + return this.__upForkSpeed * (worldModel.state.runState.timeRate ?? 1) + } + private get rotationSpeed() { + return this.__rotationSpeed * (worldModel.state.runState.timeRate ?? 1) + } + + public get fm600Entity(): FM600Entity { + if (!this._fm600Entity) { + const fm600: FM600Entity = Model.getFm600(this.item.id) as FM600Entity + this._fm600Entity = fm600 + } + return this._fm600Entity + } + + constructor(item: ItemJson, viewport: Viewport, option?: RendererCudOption) { + super(item, viewport) + + if (!FM6003DGraphics.fm600PedestalGeometry) { + FM6003DGraphics.fm600PedestalGeometry = FM6003DGraphics.createFm600Pedestal() + } + // "#ff3232" + const fm600PedestalGeometry = FM6003DGraphics.fm600PedestalGeometry + const fm600PedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffababab}) + const fm600PedestalMesh = new THREE.Mesh(fm600PedestalGeometry, fm600PedestalMaterial) + const groupPedestal = new THREE.Group() + groupPedestal.name = 'fm600Pedestal' + groupPedestal.add(fm600PedestalMesh) + + if (!FM6003DGraphics.fm600HeaderGeometry) { + FM6003DGraphics.fm600HeaderGeometry = FM6003DGraphics.createFm600Header() + } + const fm600HeaderGeometry = FM6003DGraphics.fm600HeaderGeometry + const fm600HeaderMaterial = new THREE.MeshPhongMaterial({color: 0xffff3232}) + const fm600HeaderMesh = new THREE.Mesh(fm600HeaderGeometry, fm600HeaderMaterial) + groupPedestal.add(fm600HeaderMesh) + + if (!FM6003DGraphics.fm600PlateGeometry) { + FM6003DGraphics.fm600PlateGeometry = FM6003DGraphics.createFm600Plate() + } + const fm600PlateGeometry = FM6003DGraphics.fm600PlateGeometry + // "#1f1d1d" + const fm600PlateMaterial = new THREE.MeshPhongMaterial({color: 0xff4d4d4d}) + const pfm600PlateMesh = new THREE.Mesh(fm600PlateGeometry, fm600PlateMaterial) + pfm600PlateMesh.name = 'fm600Plate' + + + + this.add(groupPedestal) + this.add(pfm600PlateMesh) + } + + /*==========动画处理============*/ + + + // 取货 + override addLoad(height: number, goodsId: string): void { + this.actionAnimation = 'wq' + this.TaskMode = 2 + this.OperationType = 1 + this.animationUpPlate(0.1).then( + () => { + try { + // 将物品拾取到机械臂上 + const mesh = this.pickupItem(goodsId) + mesh.position.set(0, 0, -0.15) + mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0) + this.getArmObject().add(mesh) + } catch (e) { + console.error(e) + } + } + ) + } + + // 卸货 + override addUnload(height: number, goodsId: string): void { + this.actionAnimation = 'wq' + this.TaskMode = 2 + this.OperationType = 2 + this.animationDownPlate().then( + () => { + try { + const a = this.agvStatusVo.unloadBasLocationVo + // 将物品从机械臂上卸下 + this.dropItem(goodsId, a.rack, a.bay, a.level, a.cell) + } catch (e) { + console.error(e) + } + } + ) + } + + animationUpPlate(y: number): Promise { + + const fm600Plate = this.getObjectByName('fm600Plate') + const time = Math.abs((fm600Plate.position.y - y) / this.upForkSpeed) + + return new Promise(resolve => { + const bh = 0.32 + const children = fm600Plate.children + + this.actionAnimation = gsap.to(fm600Plate.position, { + y: y, + duration: time, + repeat: 0, + ease: 'sine.inOut', + onComplete: ()=>{ + setTimeout(() => { + resolve() + }, 1000 / (worldModel.state.runState.timeRate ?? 1)) + }, + onUpdate: function () { + const a = this.targets()[0] + if (a.y < bh) { + if (a.y < 0) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + child.position.y = 0 - a.y + } + } + } + } + }) + }) + } + + + override animationDownPlate(): Promise { + return this.animationUpFork(0) + } + + override getArmObject(): THREE.Object3D | undefined { + return this.getObjectByName('fm600Plate') + } +} + + diff --git a/src/modules/amr/fm600/FM600Entity.ts b/src/modules/amr/fm600/FM600Entity.ts new file mode 100644 index 0000000..98a050a --- /dev/null +++ b/src/modules/amr/fm600/FM600Entity.ts @@ -0,0 +1,120 @@ +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' +import FM6003dObject from "./FM6003dObject"; +/** + * CL2 机械臂实体类 + * 0.4m/ss // a max 1.2m/s + * 90 = 3.5s cl2 + * 90 = 5s // cLX + */ +export default class FM600Entity extends BaseEntity { + constructor(viewport: Viewport, id: string) { + super(viewport, id) + } + + + + + // 抬 + addArmRaise(height: number) { + // super.addArmRaise(height) + this.taskQueue.add(this.createTask('ARM_RAISE', + () => this.cl2Object.animationUpFork(height) + )) + } + + // 降 + addArmLower() { + // super.addArmLower() + this.taskQueue.add(this.createTask('ARM_LOWER', + () => this.cl2Object.animationDownFork() + )) + } + + // 伸 + addArmExtender(z: number = 1.2) { + // super.addArmExtender() + this.taskQueue.add(this.createTask('ARM_EXTEND', + () => this.cl2Object.animationShowFork(z) + )) + } + + // 缩 + addArmRetractor() { + // super.addArmRetractor() + this.taskQueue.add(this.createTask('ARM_RETRACT', + () => this.cl2Object.animationHideFork() + )) + } + // 装 + addLoad(item: string): void { + + const ptrForkMesh = this.getArmObject() + this.taskQueue.add(this.createTask('LOAD', async () => { + // 实际业务中应包含装载逻辑 + this.isCarrying = true + this.isLoading = true + // 将物品拾取到机械臂上 + const mesh = this.pickupItem(item) + mesh.position.set(0, 0, -0.15) + mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0) + ptrForkMesh.add(mesh) + })) + + this.taskQueue.add(this.createTask('ARM_RAISE', + ()=>{ + return new Promise(resolve => { + gsap.to(ptrForkMesh.position, { + y: ptrForkMesh.position.y + 0.2, + duration: 1, + ease: 'sine.inOut', + onComplete: () => { + this.isCarrying = true + this.isLoading = false + resolve() + } + }) + }) + })) + } + + // 卸 + 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 + } + + get cl2Object(): FM6003dObject { + return this.object as FM6003dObject + } +} diff --git a/src/modules/amr/fm600/FM600Interaction.ts b/src/modules/amr/fm600/FM600Interaction.ts new file mode 100644 index 0000000..26639a0 --- /dev/null +++ b/src/modules/amr/fm600/FM600Interaction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class FM600Interaction 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/amr/fm600/FM600PropertySetter.ts b/src/modules/amr/fm600/FM600PropertySetter.ts new file mode 100644 index 0000000..2cc8a18 --- /dev/null +++ b/src/modules/amr/fm600/FM600PropertySetter.ts @@ -0,0 +1,24 @@ +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: {}, + }, + { + dataPath: 'state', label: 'ptr控制', input: 'PtrController', + inputProps: {}, + }, + ], + }, +}; + +export default propertySetter; diff --git a/src/modules/amr/fm600/FM600Renderer.ts b/src/modules/amr/fm600/FM600Renderer.ts new file mode 100644 index 0000000..1970056 --- /dev/null +++ b/src/modules/amr/fm600/FM600Renderer.ts @@ -0,0 +1,122 @@ +import * as THREE from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import Constract from '@/core/Constract.ts' +import FM6003dObject from './FM6003dObject' +import type { Object3DLike } from '@/types/ModelTypes.ts' +import type Viewport from '@/core/engine/Viewport.ts' + +/** + * ptr侧叉渲染器 + */ +export default class FM600Renderer extends BaseRenderer { + static POINT_NAME = 'fm600' + + pointMaterial: THREE.Material + + /** + * 默认点的高度, 防止和地面重合 + */ + readonly defulePositionY: number = Constract.HEIGHT_WAY + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1.5, 1.98, 1.5) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + /** + * 如果某物品要放到 Cl2 上 返回可存放的位置和角度一个 Position 和 Rotation + */ + getStorePlacement(storeItem: ItemJson, bay = 0, level = 0, cell = 0) + : { + position: [number, number, number], rotation: [number, number, number], + getParentObject3D?: (viewport: Viewport, parent: THREE.Object3D) => THREE.Object3D + } { + + return { + position: [0, 0.2, -0.15], + rotation: [0, 90, 0], + getParentObject3D: this.getArmObject.bind(this) + } + } + + getArmObject(viewport: Viewport, item: ItemJson): THREE.Object3D { + // 获取机械臂对象 + const object = viewport.entityManager.findObjectById(item.dt.storeAt?.item) + if (!object) { + console.warn('PtrRenderer: getArmObject failed, not found Cl2:', item.dt.storeAt?.item) + return + } + + const agv = object as THREE.Group + if (agv.children.length > 1) { + const pillar = agv.children[1] + if (pillar.children.length > 1) { + return pillar.children[1] + } + } + } + + /** + * 所有的点,必须使用 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 { + // 创建平面几何体 + const group = new FM6003dObject(item, this.tempViewport, option) + group.name = FM600Renderer.POINT_NAME + + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + return group + } + + updatePoint(item: ItemJson, object: Object3DLike, option?: RendererCudOption): Object3DLike { + const group: THREE.Group = object as THREE.Group + + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + group.rotation.set( + THREE.MathUtils.degToRad(item.tf[1][0]), + THREE.MathUtils.degToRad(item.tf[1][1]), + THREE.MathUtils.degToRad(item.tf[1][2]) + ) + + // 禁止缩放, + item.tf[2][0] = this.defaultScale.x + item.tf[2][1] = this.defaultScale.y + item.tf[2][2] = this.defaultScale.z + + return group + } + + dispose() { + super.dispose() + this.pointMaterial?.dispose() + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + throw new Error('Ptr createPointBasic not allow!') + } + +} diff --git a/src/modules/amr/fm600/index.ts b/src/modules/amr/fm600/index.ts new file mode 100644 index 0000000..da5b335 --- /dev/null +++ b/src/modules/amr/fm600/index.ts @@ -0,0 +1,12 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import FM600Renderer from './FM600Renderer.ts' +import FM600Interaction from './FM600Interaction.ts' +import propertySetter from './FM600PropertySetter.ts' + +export const ITEM_TYPE_NAME = 'fm600' + +export default defineModule(ITEM_TYPE_NAME, () => ({ + renderer: new FM600Renderer(ITEM_TYPE_NAME), + interaction: new FM600Interaction(ITEM_TYPE_NAME), + setter: propertySetter +})) diff --git a/src/modules/amr/ptr/PtrObject.ts b/src/modules/amr/ptr/PtrObject.ts index 211a73e..669c6a8 100644 --- a/src/modules/amr/ptr/PtrObject.ts +++ b/src/modules/amr/ptr/PtrObject.ts @@ -661,7 +661,7 @@ export default class PtrObject extends THREE.Object3D { 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) { + } else if ((data.OperationType == 1 || data.OperationType == 2 || data.OperationType == 4) && data.GoodsSlotDirection >= 0 && data.GoodsSlotDirection <= 3) { if (data.GoodsSlotDirection == 0) { endDirection = 3 } else { @@ -692,6 +692,41 @@ export default class PtrObject extends THREE.Object3D { } } + if (data.OperationType == 1) { + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: 'LOAD', + OperationType: 1, + 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 == 2) { + + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: 'UNLOAD', + OperationType: 2, + 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 = { @@ -983,7 +1018,7 @@ export default class PtrObject extends THREE.Object3D { } // 取货 - addLoad(height: number, goodsId: string): void { + override addLoad(height: number, goodsId: string): void { this.actionAnimation = 'wq' this.TaskMode = 2 this.PickMode = 1 @@ -1015,7 +1050,7 @@ export default class PtrObject extends THREE.Object3D { } // 卸货 - addUnload(height: number, goodsId: string): void { + override addUnload(height: number, goodsId: string): void { this.actionAnimation = 'wq' this.TaskMode = 2 this.PickMode = 2 diff --git a/src/types/Model.d.ts b/src/types/Model.d.ts index dcdd001..5b00f1d 100644 --- a/src/types/Model.d.ts +++ b/src/types/Model.d.ts @@ -34,6 +34,11 @@ declare interface Model { */ getClx(id: string): ClxIf + /** + * 根据ID获取 FM600 车控制器 + */ + getFm600(id: string): Fm600If + /** * 在指定位置创建一个流动的库存物品 @@ -398,6 +403,9 @@ interface Cl2If extends EntityIf, Carry, Walk, ForkArm, LiftingArm { interface ClxIf extends EntityIf, Carry, Walk, ForkArm, LiftingArm { } +interface Fm600If extends EntityIf, Carry, Walk, ForkArm, LiftingArm { +} + interface CustomScript { /** * 脚本名称