import * as THREE from 'three' import BaseRenderer from '@/core/base/BaseRenderer.ts' import { decimalSumBy } from '@/core/ModelUtils' import Constract from '@/core/Constract.ts' import type Viewport from '@/core/engine/Viewport.ts' import Rack3dObject from '@/modules/rack/Rack3dObject' /** * 货架货位渲染器 */ export default class RackRenderer extends BaseRenderer { static POINT_NAME = 'rack' 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.05 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]) ) } createLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { // throw new Error('not allow store line.') } createLineBasic(start: ItemJson, end: ItemJson, type: LinkType) { // throw new Error('not allow store line.') } updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { // throw new Error('not allow store line.') } /** * 获取物品存放在货架后的位置和旋转角度 * item.dt.storeAt 必须存在, 比如 { item:'货架ID', bay:0, level:1, cell:0 } */ getStorePlacement(rack: ItemJson, bay = 0, level = 0, cell = 0): { position: [number, number, number], rotation: [number, number, number] } { // if (!item.dt?.storeAt?.item) { // // 没有定义存储位置,返回空对象 // //@ts-ignore // return {} // } // // const bay = item.dt?.storeAt?.bay || 0 // const level = item.dt?.storeAt?.level || 0 // 暂时不用算格子 const cell = item.dt?.storeAt?.cell || 0 // 目标货架 // const rack = viewport.stateManager.findItemById(item.dt.storeAt.item) const rackWidth = decimalSumBy(rack.dt.bays, (b: any) => b.bayWidth) const bays = rack.dt.bays const levelHeights = rack.dt.bays[bay]?.levelHeight // 局部坐标系下的偏移量 let localX = 0 for (let i = 0; i < bay; i++) { localX += bays[i]?.bayWidth || 0 } localX += bays[bay].bayWidth / 2 // 居中 let localY = 0 for (let i = 0; i <= level; i++) { localY += levelHeights[i] } // 构建局部偏移向量 const rackPos = new THREE.Vector3(...rack.tf[0]) const offset = new THREE.Vector3( localX - rackWidth / 2, // 相对坐标从最左边开始, localY, 0) // 应用货架的旋转 const q = new THREE.Quaternion() q.setFromEuler(new THREE.Euler( THREE.MathUtils.degToRad(rack.tf[1][0]), THREE.MathUtils.degToRad(rack.tf[1][1]), THREE.MathUtils.degToRad(rack.tf[1][2]) )) offset.applyQuaternion(q) const worldPosition = rackPos.clone().add(offset) return { position: [ worldPosition.x, worldPosition.y + rack.dt.bottomBarHeight + 0.05, // 加上横梁高度 worldPosition.z ], rotation: [ // 托盘旋转90度才能放进货架 rack.tf[1][0], rack.tf[1][1] + 90, rack.tf[1][2] ] } } updatePoint(item: ItemJson, group: THREE.Group, option?: RendererCudOption): 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]) ) // group.scale 不允许修改!! // 禁止缩放, item.tf[2][0] = item.dt.rackWidth item.tf[2][1] = item.dt.rackHeight // 更新放在内部的所有箱子 const subItems = this.tempViewport.runtimeManager.getItemsByRack(item.id) const viewport = this.tempViewport if (subItems) { _.defer(() => { viewport.stateManager.update(({ getEntity, putEntity, deleteEntity, addEntity }) => { for (const subItemId of subItems) { const subItem = getEntity(subItemId) if (subItem) { const { position, rotation } = this.getStorePlacement(item, subItem.dt.storeAt.bay, subItem.dt.storeAt.level, subItem.dt.storeAt.cell) if (position) { subItem.tf[0][0] = position[0] subItem.tf[0][1] = position[1] subItem.tf[0][2] = position[2] subItem.tf[1][0] = rotation[0] subItem.tf[1][1] = rotation[1] subItem.tf[1][2] = rotation[2] putEntity(subItem) } } } }) }) } return group } createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { // 创建平面几何体 if (!item.dt.bays || !item.dt.rackDepth) { system.showErrorDialog('RackRenderer field bays / rackDepth is null!') return null } const group = new Rack3dObject(item, option) group.name = RackRenderer.POINT_NAME const rackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth) const heights = [] for (let i = 0; i < item.dt.bays.length; i++) { const bay = item.dt.bays[i] const bayHeight = decimalSumBy(bay.levelHeight) heights.push(bayHeight) } const rackHeight = _.max(heights) // 设置位置 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.dt.rackWidth = rackWidth item.dt.rackHeight = rackHeight item.tf[2][0] = item.dt.rackWidth item.tf[2][1] = item.dt.rackHeight return group } dispose() { super.dispose() this.pointMaterial?.dispose() } }