diff --git a/src/assets/Models/shuttle.glb b/src/assets/Models/shuttle.glb new file mode 100644 index 0000000..45d1b62 Binary files /dev/null and b/src/assets/Models/shuttle.glb differ diff --git a/src/core/manager/WorldModel.ts b/src/core/manager/WorldModel.ts index 3378f36..b169a04 100644 --- a/src/core/manager/WorldModel.ts +++ b/src/core/manager/WorldModel.ts @@ -5,6 +5,7 @@ import Measure from '@/modules/measure' import Way from '@/modules/way' import Gstore from '@/modules/gstore' import Rack from '@/modules/rack' +import ShuttleRack from '@/modules/shuttle_rack' import Pallet from "@/modules/pallet" import Tote from "@/modules/tote" import Carton from "@/modules/carton" @@ -73,6 +74,7 @@ export default class WorldModel { Way, Gstore, Rack, + ShuttleRack, Pallet, Tote, Carton, diff --git a/src/example/example1.js b/src/example/example1.js index aff836d..a4097c9 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -471,17 +471,21 @@ export default { catalogCode: 'shuttle1', t: 'floor', items: [ { + id: 'shuttle_rack1', t: 'shuttle_rack', // 多穿库货架 + tf: [[0, 0.1, 0], [0, 0, 0], [1, 1, 1]], dt: { bayCount: 15, // 列数 - aisleCount: 2, // 巷道数 + aisleCount: 16, // 巷道数 hideFloor: false, // 隐藏底板 + shuttleRackDepth:7.6, bayRail: [ // 横行巷道 { afterBay: 2, // 横行巷道所在位置 railWidth: 1.4 // 横行巷道宽度 }, - { afterBay: 11, railWidth: 1.4 } + { afterBay: 15, railWidth: 1.4 }, + { afterBay: 26, railWidth: 1.4 } ], bays: [ // 每列的配置 { bayWidth: 1.6 }, @@ -500,6 +504,24 @@ export default { { bayWidth: 1.6 }, { bayWidth: 1.6 }, { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, + { bayWidth: 1.6 }, { bayWidth: 1.6 } ], aisles: [ // 巷道的配置 @@ -507,14 +529,14 @@ export default { rail: { railWidth: 0.8 }, // 轨道, 主要是轨道宽度 railLeft: { // 轨道左侧 stores: [ // 轨道左侧货位 - { depth: 1.1 }, // 深位1的存储深度 - { depth: 1.1 } // 深位2的存储深度 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 ] }, railRight: { // 轨道左侧 stores: [ // 轨道左侧货位 - { depth: 1.1 }, // 深位1的存储深度 - { depth: 1.1 } // 深位2的存储深度 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 ] } }, @@ -522,14 +544,224 @@ export default { rail: { railWidth: 0.8 }, railLeft: { stores: [ - { depth: 1.1 }, - { depth: 1.1 } + { depth: 0.75 }, + { depth: 0.75 } ] }, railRight: { stores: [ - { depth: 1.1 }, - { depth: 1.1 } + { depth: 0.75 }, + { depth: 0.75 } + ] + } + }, + { + rail: { railWidth: 0.8 }, // 轨道, 主要是轨道宽度 + railLeft: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + }, + railRight: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + } + }, + { + rail: { railWidth: 0.8 }, + railLeft: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + }, + railRight: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + } + }, + { + rail: { railWidth: 0.8 }, // 轨道, 主要是轨道宽度 + railLeft: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + }, + railRight: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + } + }, + { + rail: { railWidth: 0.8 }, + railLeft: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + }, + railRight: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + } + }, + { + rail: { railWidth: 0.8 }, // 轨道, 主要是轨道宽度 + railLeft: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + }, + railRight: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + } + }, + { + rail: { railWidth: 0.8 }, + railLeft: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + }, + railRight: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + } + }, + { + rail: { railWidth: 0.8 }, // 轨道, 主要是轨道宽度 + railLeft: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + }, + railRight: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + } + }, + { + rail: { railWidth: 0.8 }, + railLeft: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + }, + railRight: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + } + }, + { + rail: { railWidth: 0.8 }, // 轨道, 主要是轨道宽度 + railLeft: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + }, + railRight: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + } + }, + { + rail: { railWidth: 0.8 }, + railLeft: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + }, + railRight: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + } + }, + { + rail: { railWidth: 0.8 }, // 轨道, 主要是轨道宽度 + railLeft: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + }, + railRight: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + } + }, + { + rail: { railWidth: 0.8 }, + railLeft: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + }, + railRight: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + } + }, + { + rail: { railWidth: 0.8 }, // 轨道, 主要是轨道宽度 + railLeft: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + }, + railRight: { // 轨道左侧 + stores: [ // 轨道左侧货位 + { depth: 0.75 }, // 深位1的存储深度 + { depth: 0.75 } // 深位2的存储深度 + ] + } + }, + { + rail: { railWidth: 0.8 }, + railLeft: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } + ] + }, + railRight: { + stores: [ + { depth: 0.75 }, + { depth: 0.75 } ] } } diff --git a/src/modules/shuttle_rack/ShuttleRackEntity.ts b/src/modules/shuttle_rack/ShuttleRackEntity.ts new file mode 100644 index 0000000..a2a0b05 --- /dev/null +++ b/src/modules/shuttle_rack/ShuttleRackEntity.ts @@ -0,0 +1,5 @@ +import BaseEntity from '@/core/base/BaseItemEntity.ts' + +export default class ShuttleRackEntity extends BaseEntity { + +} diff --git a/src/modules/shuttle_rack/ShuttleRackInteraction.ts b/src/modules/shuttle_rack/ShuttleRackInteraction.ts new file mode 100644 index 0000000..fedca98 --- /dev/null +++ b/src/modules/shuttle_rack/ShuttleRackInteraction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class ShuttleRackInteraction 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.rackWidth = 3 // 宽度 + item.dt.rackDepth = 1 // 深度 + return item + } +} diff --git a/src/modules/shuttle_rack/ShuttleRackPropertySetter.ts b/src/modules/shuttle_rack/ShuttleRackPropertySetter.ts new file mode 100644 index 0000000..9e80d21 --- /dev/null +++ b/src/modules/shuttle_rack/ShuttleRackPropertySetter.ts @@ -0,0 +1,65 @@ +import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; +import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; + +const propertySetter: PropertySetter = { + flatten: { + fields: [ + ...basicFieldsSetter, + { + dataPath: 'dt.rackDepth', label: '货架深度', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.levelCount', label: '总层数', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.bayCount', label: '总列数', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.hideFloor', label: '隐藏底板', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.extendColumns', label: '扩展挡板', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.columnSpacing', label: '支脚跨越', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.bays', input: 'BayEditor', + inputProps: { + }, + }, + /** + * dt.bays 5列3层货架示例 + * { + * dt: { + * rackDepth: 1.1, // 货架深度 + * levelCount: 3, // 总层数 + * bayCount: 5, // 总列数 + * hideFloor: false, // 隐藏底板 + * extendColumns: true, // 扩展挡板 + * columnSpacing: 1, // 支脚跨越 + * bays: [ // 每列的配置 + * { + * bayWidth: 1.6, // 列的宽度 + * offset: // 列偏移 + * levelHeight: [ 1.4, 1.4, 1.4 ] // 每层的高度 + * }, + * {bayWidth: 1.6, levelHeight: [ 1.4, 1.4, 1.4 ]}, + * {bayWidth: 1.6, levelHeight: [ 1.4, 1.4, 1.4 ]}, + * {bayWidth: 1.6, levelHeight: [ 1.4, 1.4, 1.4 ]}, + * {bayWidth: 1.6, levelHeight: [ 1.4, 1.4, 1.4 ]}, + * ] + * } + * } + */ + ], + }, +}; + +export default propertySetter; diff --git a/src/modules/shuttle_rack/ShuttleRackRenderer.ts b/src/modules/shuttle_rack/ShuttleRackRenderer.ts new file mode 100644 index 0000000..7d46380 --- /dev/null +++ b/src/modules/shuttle_rack/ShuttleRackRenderer.ts @@ -0,0 +1,1022 @@ +import * as THREE from 'three' +import { BufferGeometry } from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import { decimalSumBy } from '@/core/ModelUtils' +import Constract from '@/core/Constract.ts' +import Plastic_Rough_JPG from '@/assets/Models/Plastic_Rough.jpg' +import storageBar_PNG from '@/assets/Models/storageBar.png' +import { Material } from 'three/src/materials/Material' +import { InstancedMesh } from 'three/src/objects/InstancedMesh' +//@ts-ignore +import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js' + +/** + * 货架货位渲染器 + */ +export default class ShuttleRackRenderer extends BaseRenderer { + static POINT_NAME = 'shuttle_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]) + ) + } + + + 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.bays || !item.dt.aisles) { + system.showErrorDialog('RackRenderer field bays / aisles is null!') + return null + } + + const group = new THREE.Group() + group.name = ShuttleRackRenderer.POINT_NAME + + const meshes = this.createShuttleRack(item, option) + + meshes.forEach(mesh => { + group.add(mesh) + }) + + + // 设置位置 + 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]) + ) + return group + } + + dispose() { + super.dispose() + this.pointMaterial?.dispose() + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + throw new Error('Rack createPointBasic not allow!') + } + + shuttleRackVerticalBarWidth = 0.1 + shuttleRackVerticalBarDepth = 0.08 + shuttleRackVerticalBarColor = 0xFF35499C + shuttleRackVerticalBarGeometry: BufferGeometry = null + shuttleRackVerticalBarMaterial: Material = null + + shuttleRackLinkBarColor = 0xFF35499C + shuttleRackLinkBarGeometry: BufferGeometry = null + shuttleRackLinkBarMaterial: Material = null + + shuttleRackHorizontalBarWidth = 0.1 + shuttleRackHorizontalBarDepth = 0.08 + shuttleRackHorizontalBarColor = 0xFFF97F27 + shuttleRackHorizontalBarGeometry: BufferGeometry = null + shuttleRackHorizontalBarMaterial: Material = null + + shuttleRackPlateColor = 0xFFA5BDDC + shuttleRackPlateGeometry: BufferGeometry = null + shuttleRackPlateMaterial: Material = null + + shuttleRailwayColor = 0xFFB6B6B6 + shuttleRailwayGeometry: BufferGeometry = null + shuttleRailwayMaterial: Material = null + + c = '#b6b6b6' + bottomBarHeight = 0.2 + bottomLinkHeight = 0.2 + + vBarSectionPoints = [ + { x: -0.05, y: -0.05 }, + { x: -0.025, y: -0.05 }, + { x: -0.01, y: -0.045 }, + { x: 0.05, y: -0.045 }, + { x: 0.025, y: -0.05 }, + { x: 0.05, y: -0.05 }, + { x: 0.05, y: 0.042 }, + { x: 0.042, y: 0.05 }, + { x: 0.025, y: 0.05 }, + { x: 0.025, y: 0.042 }, + { x: 0.042, y: 0.042 }, + { x: 0.042, y: -0.042 }, + { x: -0.042, y: -0.042 }, + { x: -0.042, y: 0.042 }, + { x: -0.025, y: 0.042 }, + { x: -0.025, y: 0.05 }, + { x: -0.042, y: 0.05 }, + { x: -0.05, y: 0.042 }, + { x: -0.05, y: -0.05 } + ] + hBarSectionPoints = [ + { x: -0.05, y: -0.05 }, + { x: -0.025, y: -0.05 }, + { x: -0.01, y: -0.045 }, + { x: 0.05, y: -0.045 }, + { x: 0.025, y: -0.05 }, + { x: 0.05, y: -0.05 }, + { x: 0.05, y: 0.05 }, + { x: 0.025, y: 0.05 }, + { x: 0.01, y: 0.045 }, + { x: -0.01, y: 0.045 }, + { x: -0.025, y: 0.05 }, + { x: -0.05, y: 0.05 }, + { x: -0.05, y: -0.05 } + ] + + linkSectionPoints = [ + { x: -0.05, y: -0.05 }, + { x: -0.05, y: 0.05 }, + { x: 0, y: 0.05 }, + { x: 0, y: 0.06 }, + { x: -0.06, y: 0.06 }, + { x: -0.06, y: -0.05 }, + { x: -0.05, y: -0.05 } + ] + + linkBarSectionPoints = [ + { x: -0.025, y: -0.025 }, + { x: 0.025, y: -0.025 }, + { x: 0.025, y: 0.025 }, + { x: -0.025, y: 0.025 }, + { x: -0.025, y: -0.025 } + ] + + createVerticalBar(x, y, z, length): THREE.BufferGeometry { + + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape() + shape.moveTo(this.vBarSectionPoints[0].x, this.vBarSectionPoints[0].y) + for (let i = 1; i < this.vBarSectionPoints.length; i++) { + shape.lineTo(this.vBarSectionPoints[i].x, this.vBarSectionPoints[i].y) + } + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, length, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + // 调整uv方便正确贴图 + this.resetUVs(geometry) + return geometry + } + + createVerticalBarMaterial(): THREE.Material { + + let textureLoader = new THREE.TextureLoader() + + // 加载纹理 + const textureHole = textureLoader.load(storageBar_PNG) // 孔洞 + const textureMaterial = textureLoader.load(Plastic_Rough_JPG) // 表面材质 + + textureHole.repeat.set(10, 18) // X轴重复,Y轴重复 + textureMaterial.repeat.set(2, 2) // X轴重复,Y轴重复 + // textureHole.offset.set(0.5, 0) + // textureHole.center.set(0.5, 0) + + // 必须设置包裹模式为重复 + textureHole.wrapS = THREE.RepeatWrapping + textureHole.wrapT = THREE.RepeatWrapping + textureMaterial.wrapS = THREE.RepeatWrapping + textureMaterial.wrapT = THREE.RepeatWrapping + + const material = new THREE.MeshPhongMaterial() + material.alphaMap = textureHole + material.normalMap = textureMaterial + material.color.setHex(this.shuttleRackVerticalBarColor, 'srgb') + material.specular.setHex(0xff6d6d6d, 'srgb') + material.transparent = true + material.needsUpdate = true + + return material + } + + createLinkBar(x, y, z, vBarLength, depth, bottomDistance, topDistance): THREE.BufferGeometry { + + const bgs: BufferGeometry[] = [] + const top = vBarLength - topDistance + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape() + shape.moveTo(this.linkBarSectionPoints[0].x, this.linkBarSectionPoints[0].y) + for (let i = 1; i < this.linkBarSectionPoints.length; i++) { + shape.lineTo(this.linkBarSectionPoints[i].x, this.linkBarSectionPoints[i].y) + } + + // 拉伸轨迹线 横向 底部 + const curveHBottom = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, bottomDistance, 0), new THREE.Vector3(0, bottomDistance, depth)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsHBottom = { + steps: 1, + bevelEnabled: false, + extrudePath: curveHBottom // 设置挤出轨迹 + } + + // 拉伸轨迹线 横向 底部 + const curveHTop = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, top, 0), new THREE.Vector3(0, top, depth)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsHTop = { + steps: 1, + bevelEnabled: false, + extrudePath: curveHTop // 设置挤出轨迹 + } + + // 创建挤出几何体 + const geometryHBottom = new THREE.ExtrudeGeometry(shape, optionsHBottom) + const geometryHTop = new THREE.ExtrudeGeometry(shape, optionsHTop) + bgs.push(geometryHBottom, geometryHTop) + + let remainingHeight = vBarLength - bottomDistance - topDistance + + // 需要创建斜杆 + + for (let i = 0; i < Math.floor(remainingHeight / depth); i++) { + // 拉伸轨迹线 斜向 + const curveD = new THREE.CatmullRomCurve3( + (i % 2 == 0) ? [new THREE.Vector3(0, bottomDistance + depth * i, 0), new THREE.Vector3(0, bottomDistance + depth * (i + 1), depth)] + : [new THREE.Vector3(0, bottomDistance + depth * (i + 1), 0), new THREE.Vector3(0, bottomDistance + depth * (i), depth)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + const optionsD = { + steps: 1, + bevelEnabled: false, + extrudePath: curveD // 设置挤出轨迹 + } + + const geometryD = new THREE.ExtrudeGeometry(shape, optionsD) + bgs.push(geometryD) + } + + if (vBarLength - bottomDistance - topDistance > depth) { + + + } + + // 调整uv方便正确贴图 + // this.resetUVs(geometry); + return mergeGeometries(bgs) + } + + createLinkBarMaterial(): THREE.Material { + + const material = new THREE.MeshPhongMaterial() + material.color.setHex(this.shuttleRackLinkBarColor, 'srgb') + material.specular.setHex(0xff6d6d6d, 'srgb') + material.transparent = true + material.needsUpdate = true + + return material + } + + createHorizontalBar(x, y, z, length): THREE.BufferGeometry { + const geometries: THREE.BufferGeometry[] = [] + // 创建一个形状 柱子的截面形状 + const shape = new THREE.Shape() + shape.moveTo(this.hBarSectionPoints[0].x, this.hBarSectionPoints[0].y) + for (let i = 1; i < this.hBarSectionPoints.length; i++) { + shape.lineTo(this.hBarSectionPoints[i].x, this.hBarSectionPoints[i].y) + } + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0.05, 0, 0), new THREE.Vector3(length - 0.05, 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + geometries.push(geometry) + + const linkShapeL = new THREE.Shape() + const linkShapeR = new THREE.Shape() + linkShapeL.moveTo(this.linkSectionPoints[0].x, this.linkSectionPoints[0].y) + linkShapeR.moveTo(this.linkSectionPoints[0].x + (length), this.linkSectionPoints[0].y) + for (let i = 1; i < this.linkSectionPoints.length; i++) { + linkShapeL.lineTo(this.linkSectionPoints[i].x, this.linkSectionPoints[i].y) + linkShapeR.lineTo(this.linkSectionPoints[i].x + (length), this.linkSectionPoints[i].y) + } + + // 拉伸轨迹线 + const linkCurve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0, -0.08), new THREE.Vector3(0, 0, 0.08)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const linkOptions = { + steps: 1, + bevelEnabled: false, + extrudePath: linkCurve // 设置挤出轨迹 + } + // 创建挤出几何体 + const linkGeometryL = new THREE.ExtrudeGeometry(linkShapeL, linkOptions) + linkGeometryL.rotateZ(-Math.PI / 2) + const linkGeometryR = new THREE.ExtrudeGeometry(linkShapeR, linkOptions) + linkGeometryR.rotateX(-Math.PI) + linkGeometryR.rotateZ(-Math.PI / 2) + + geometries.push(linkGeometryL) + geometries.push(linkGeometryR) + + // 调整uv方便正确贴图 + // this.resetUVs(geometry); + return mergeGeometries(geometries) + } + + createHorizontalBarMaterial(): THREE.Material { + + const material = new THREE.MeshPhongMaterial() + material.color.setHex(this.shuttleRackHorizontalBarColor, 'srgb') + material.specular.setHex(0xff6d6d6d, 'srgb') + material.transparent = true + material.needsUpdate = true + + return material + } + + createPlate(x, y, z, isPlate): THREE.BufferGeometry { + + const length = 1, depth = 1; + + const geometries: THREE.BufferGeometry[] = [] + + if (isPlate) { + const shapeZ = new THREE.Shape() + shapeZ.moveTo(-(depth)/2, -(length-0.1)/2) + shapeZ.lineTo((depth)/2, -(length-0.1)/2) + shapeZ.lineTo((depth)/2, (length-0.1)/2) + shapeZ.lineTo(-(depth)/2, (length-0.1)/2) + shapeZ.lineTo(-(depth)/2, -(length-0.1)/2) + + const curveZ = new THREE.CatmullRomCurve3( + [new THREE.Vector3(length/2, depth/2, -0.05), new THREE.Vector3(length/2, depth/2, -0.055)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + // 挤出几何图形 参数 + const optionsZ = { + steps: 1, + bevelEnabled: false, + extrudePath: curveZ // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometryZ = new THREE.ExtrudeGeometry(shapeZ, optionsZ) + geometries.push(geometryZ) + } else { + // 间隔 0.015 到 0.020 + const len = length - 0.1 - 0.2 + const a = length/0.15 + const b = length/0.20 + let c = Math.floor((a+b) / 2) + if (len/c > 0.2) { + c += 1 + } + const dlen = len/c + for (let i = 0; i <= c; i++) { + + const shapeZ = new THREE.Shape() + shapeZ.moveTo(this.linkBarSectionPoints[0].x, this.linkBarSectionPoints[0].y) + for (let i = 1; i < this.linkBarSectionPoints.length; i++) { + shapeZ.lineTo(this.linkBarSectionPoints[i].x, this.linkBarSectionPoints[i].y) + } + + const curveZ = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0.15 + dlen * i, 0, -0.024), new THREE.Vector3(0.15 + dlen * i, depth, -0.024)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + // 挤出几何图形 参数 + const optionsZ = { + steps: 1, + bevelEnabled: false, + extrudePath: curveZ // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometryZ = new THREE.ExtrudeGeometry(shapeZ, optionsZ) + geometries.push(geometryZ) + } + } + + return mergeGeometries(geometries) + } + + createPlateMaterial(): THREE.Material { + + const material = new THREE.MeshPhongMaterial() + material.color.setHex(this.shuttleRackPlateColor, 'srgb') + material.specular.setHex(0xff6d6d6d, 'srgb') + material.transparent = true + material.needsUpdate = true + + return material + } + + createRailway(x, y, z): THREE.BufferGeometry { + + const shape = new THREE.Shape() + shape.moveTo(this.linkBarSectionPoints[0].x, this.linkBarSectionPoints[0].y) + for (let i = 1; i < this.linkBarSectionPoints.length; i++) { + shape.lineTo(this.linkBarSectionPoints[i].x, this.linkBarSectionPoints[i].y) + } + + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0, -0.5), new THREE.Vector3(0, 0, 0.5)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + + return geometry + } + + createRailwayMaterial(): THREE.Material { + + const material = new THREE.MeshPhongMaterial() + material.color.setHex(this.shuttleRailwayColor, 'srgb') + material.specular.setHex(0xff6d6d6d, 'srgb') + material.transparent = true + material.needsUpdate = true + + return material + } + + createShuttleRack(item: ItemJson, option?: RendererCudOption): InstancedMesh[] { + if (!item.dt.bays || !item.dt.aisles) { + system.showErrorDialog('RackRenderer field bays / aisles is null!') + return null + } + + const shuttleRackPoint = { + x: item.tf[0][0], + y: item.tf[0][1], + z: item.tf[0][2] + } + + const shuttleRackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth) + decimalSumBy(item.dt.bayRail, (b: any) => b.railWidth) + + const shuttleRackDepth = decimalSumBy(item.dt.aisles, (b: any) => b.rail.railWidth) + + decimalSumBy(item.dt.aisles, (b: any) => decimalSumBy(b.railLeft.stores, (b: any) => b.depth)) + + decimalSumBy(item.dt.aisles, (b: any) => decimalSumBy(b.railRight.stores, (b: any) => b.depth)) + + const rackHeight = 1 + + // 计算立住坐标点和长度 + const vBarMatrix: { x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number }[] = [] + + // 计算 + const linkBarMatrix: { x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number }[] = [] + + const hBarMatrix: { x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number }[] = [] + + const plateMatrix: { x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number, depth: number}[] = [] + + const railwayMatrix: { x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number }[] = [] + + let distanceX = 0, distanceY = this.bottomBarHeight, distanceZ = 0 + + for (let i = -1; i < item.dt.bays.length; i++) { + distanceZ = 0 + if (i >= 0) { + const bayRail = _.find(item.dt.bayRail, (b: any) => {return b.afterBay == i}) + + if (bayRail) { + distanceX += bayRail.railWidth + for (let j = 0; j < item.dt.aisles.length; j++) { + const aisle = item.dt.aisles[j] + let rackDepth = decimalSumBy(aisle.railLeft.stores, (b: any) => b.depth) + + distanceZ += rackDepth + if (j == 0) { + vBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: distanceZ - rackDepth - shuttleRackDepth/2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: Math.PI / 2, + rz: 0, + l: rackHeight + }) + } + vBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: distanceZ - shuttleRackDepth/2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: -Math.PI / 2, + rz: 0, + l: rackHeight + }) + linkBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: i % 2 == 0 ? (distanceZ - shuttleRackDepth/2) : (distanceZ - rackDepth - shuttleRackDepth/2), + sx: 1, + sy: 1, + sz: 1, + rx: 0, + ry: i % 2 == 0 ? Math.PI : 0, + rz: 0, + l: rackHeight + }) + + + + distanceZ += aisle.rail.railWidth + + rackDepth = decimalSumBy(aisle.railRight.stores, (b: any) => b.depth) + distanceZ += rackDepth + vBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: distanceZ - rackDepth - shuttleRackDepth/2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: Math.PI / 2, + rz: 0, + l: rackHeight + }) + vBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: distanceZ - shuttleRackDepth/2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: -Math.PI / 2, + rz: 0, + l: rackHeight + }) + linkBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: i % 2 == 0 ? (distanceZ - shuttleRackDepth/2) : (distanceZ - rackDepth - shuttleRackDepth/2), + sx: 1, + sy: 1, + sz: 1, + rx: 0, + ry: i % 2 == 0 ? Math.PI : 0, + rz: 0, + l: rackHeight + }) + + } + + railwayMatrix.push({ + x: distanceX - bayRail.railWidth - shuttleRackWidth/2 + 0.075, + y: 0.1, + z: distanceZ - shuttleRackDepth, + sx: 1, + sy: 1, + sz: shuttleRackDepth, + rx: 0, + ry: 0, + rz: 0, + l: rackHeight + }) + railwayMatrix.push({ + x: distanceX - shuttleRackWidth/2 - 0.075, + y: 0.1, + z: distanceZ - shuttleRackDepth, + sx: 1, + sy: 1, + sz: shuttleRackDepth, + rx: 0, + ry: 0, + rz: 0, + l: rackHeight + }) + + } + const bay = item.dt.bays[i] + distanceX += bay.bayWidth + } + distanceZ = 0 + for (let j = 0; j < item.dt.aisles.length; j++) { + const aisle = item.dt.aisles[j] + let rackDepth = decimalSumBy(aisle.railLeft.stores, (b: any) => b.depth) + + distanceZ += rackDepth + if (j == 0) { + vBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: distanceZ - rackDepth - shuttleRackDepth/2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: Math.PI / 2, + rz: 0, + l: rackHeight + }) + } + vBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: distanceZ - shuttleRackDepth/2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: -Math.PI / 2, + rz: 0, + l: rackHeight + }) + linkBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: i % 2 == 0 ? (distanceZ - shuttleRackDepth/2) : (distanceZ - rackDepth - shuttleRackDepth/2), + sx: 1, + sy: 1, + sz: 1, + rx: 0, + ry: i % 2 == 0 ? Math.PI : 0, + rz: 0, + l: rackHeight + }) + if (i >= 0) { + const bay = item.dt.bays[i] + if (j == 0) { + hBarMatrix.push({ + x: distanceX - bay.bayWidth - shuttleRackWidth / 2, + y: distanceY, + z: distanceZ - rackDepth - shuttleRackDepth / 2, + sx: 1, + sy: 0.8, + sz: 1, + rx: Math.PI / 2, + ry: 0, + rz: 0, + l: bay.bayWidth + }) + } + hBarMatrix.push({ + x: distanceX - bay.bayWidth- shuttleRackWidth / 2, + y: distanceY, + z: distanceZ - shuttleRackDepth / 2, + sx: 1, + sy: 0.8, + sz: 1, + rx: -Math.PI / 2, + ry: 0, + rz: 0, + l: bay.bayWidth + }) + plateMatrix.push({ + x: distanceX - bay.bayWidth - shuttleRackWidth / 2, + y: distanceY, + z: distanceZ - rackDepth - shuttleRackDepth / 2, + sx: bay.bayWidth, + sy: rackDepth, + sz: 1, + rx: Math.PI / 2, + ry: 0, + rz: 0, + l: bay.bayWidth, + depth: rackDepth + }) + + if (i == item.dt.bays.length - 1) { + railwayMatrix.push({ + x: 0, + y: 0.1, + z: distanceZ - shuttleRackDepth / 2 + 0.075, + sx: 1, + sy: 1, + sz: shuttleRackWidth, + rx: 0, + ry: Math.PI/2, + rz: 0, + l: rackHeight + }) + railwayMatrix.push({ + x: 0, + y: 0.1, + z: distanceZ + aisle.rail.railWidth - shuttleRackDepth / 2 - 0.075, + sx: 1, + sy: 1, + sz: shuttleRackWidth, + rx: 0, + ry: Math.PI/2, + rz: 0, + l: rackHeight + }) + } + + } + + distanceZ += aisle.rail.railWidth + + rackDepth = decimalSumBy(aisle.railRight.stores, (b: any) => b.depth) + distanceZ += rackDepth + vBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: distanceZ - rackDepth - shuttleRackDepth/2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: Math.PI / 2, + rz: 0, + l: rackHeight + }) + vBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: distanceZ - shuttleRackDepth/2, + sx: 0.8, + sy: 1, + sz: 1, + rx: 0, + ry: -Math.PI / 2, + rz: 0, + l: rackHeight + }) + linkBarMatrix.push({ + x: distanceX - shuttleRackWidth/2, + y: 0, + z: i % 2 == 0 ? (distanceZ - shuttleRackDepth/2) : (distanceZ - rackDepth - shuttleRackDepth/2), + sx: 1, + sy: 1, + sz: 1, + rx: 0, + ry: i % 2 == 0 ? Math.PI : 0, + rz: 0, + l: rackHeight + }) + if (i >= 0) { + const bay = item.dt.bays[i] + hBarMatrix.push({ + x: distanceX - bay.bayWidth - shuttleRackWidth / 2, + y: distanceY, + z: distanceZ - rackDepth - shuttleRackDepth / 2, + sx: 1, + sy: 0.8, + sz: 1, + rx: Math.PI / 2, + ry: 0, + rz: 0, + l: bay.bayWidth + }) + hBarMatrix.push({ + x: distanceX - bay.bayWidth- shuttleRackWidth / 2, + y: distanceY, + z: distanceZ - shuttleRackDepth / 2, + sx: 1, + sy: 0.8, + sz: 1, + rx: -Math.PI / 2, + ry: 0, + rz: 0, + l: bay.bayWidth + }) + plateMatrix.push({ + x: distanceX - bay.bayWidth - shuttleRackWidth / 2, + y: distanceY, + z: distanceZ - rackDepth - shuttleRackDepth / 2, + sx: bay.bayWidth, + sy: rackDepth, + sz: 1, + rx: Math.PI / 2, + ry: 0, + rz: 0, + l: bay.bayWidth, + depth: rackDepth + }) + } + + } + } + // 计算横梁数量 + + const meshes: InstancedMesh[] = [] + + if (vBarMatrix.length > 0) { + if (!this.shuttleRackVerticalBarGeometry) { + this.shuttleRackVerticalBarGeometry = this.createVerticalBar(vBarMatrix[0].x, vBarMatrix[0].y, vBarMatrix[0].z, vBarMatrix[0].l) + } + if (!this.shuttleRackVerticalBarMaterial) { + this.shuttleRackVerticalBarMaterial = this.createVerticalBarMaterial() + } + const dummy = new THREE.Object3D() + const vBarMesh = new THREE.InstancedMesh(this.shuttleRackVerticalBarGeometry, this.shuttleRackVerticalBarMaterial, vBarMatrix.length) + for (let i = 0; i < vBarMatrix.length; i++) { + const vp = vBarMatrix[i] + dummy.position.set(vp.x, vp.y, vp.z) + dummy.rotation.set(vp.rx, vp.ry, vp.rz) + dummy.scale.set(vp.sx, vp.sy, vp.sz) + dummy.updateMatrix() + vBarMesh.setMatrixAt(i, dummy.matrix) + } + meshes.push(vBarMesh) + } + + if (linkBarMatrix.length > 0) { + if (!this.shuttleRackLinkBarGeometry) { + this.shuttleRackLinkBarGeometry = this.createLinkBar(linkBarMatrix[0].x, linkBarMatrix[0].y, linkBarMatrix[0].z, linkBarMatrix[0].l, 1.5, this.bottomLinkHeight, 0.2) + } + if (!this.shuttleRackLinkBarMaterial) { + this.shuttleRackLinkBarMaterial = this.createLinkBarMaterial() + } + const dummy = new THREE.Object3D() + const linkBarMesh = new THREE.InstancedMesh(this.shuttleRackLinkBarGeometry, this.shuttleRackLinkBarMaterial, linkBarMatrix.length) + for (let i = 0; i < linkBarMatrix.length; i++) { + const lp = linkBarMatrix[i] + dummy.position.set(lp.x, lp.y, lp.z) + dummy.rotation.set(lp.rx, lp.ry, lp.rz) + dummy.scale.set(lp.sx, lp.sy, lp.sz) + dummy.updateMatrix() + linkBarMesh.setMatrixAt(i, dummy.matrix) + } + meshes.push(linkBarMesh) + } + + if (hBarMatrix.length > 0) { + if (!this.shuttleRackHorizontalBarGeometry) { + this.shuttleRackHorizontalBarGeometry = this.createHorizontalBar(hBarMatrix[0].x, hBarMatrix[0].y, hBarMatrix[0].z, hBarMatrix[0].l) + } + if (!this.shuttleRackHorizontalBarMaterial) { + this.shuttleRackHorizontalBarMaterial = this.createHorizontalBarMaterial() + } + const dummy = new THREE.Object3D() + const hBarMesh = new THREE.InstancedMesh(this.shuttleRackHorizontalBarGeometry, this.shuttleRackHorizontalBarMaterial, hBarMatrix.length) + for (let i = 0; i < hBarMatrix.length; i++) { + const hp = hBarMatrix[i] + dummy.position.set(hp.x, hp.y, hp.z) + dummy.rotation.set(hp.rx, hp.ry, hp.rz) + dummy.scale.set(hp.sx, hp.sy, hp.sz) + dummy.updateMatrix() + hBarMesh.setMatrixAt(i, dummy.matrix) + } + meshes.push(hBarMesh) + } + + if (plateMatrix.length > 0) { + + if (!this.shuttleRackPlateGeometry) { + this.shuttleRackPlateGeometry = this.createPlate(plateMatrix[0].x, plateMatrix[0].y, plateMatrix[0].z, false) + } + if (!this.shuttleRackPlateMaterial) { + this.shuttleRackPlateMaterial = this.createPlateMaterial() + } + const dummy = new THREE.Object3D() + const plateMesh = new THREE.InstancedMesh(this.shuttleRackPlateGeometry, this.shuttleRackPlateMaterial, plateMatrix.length) + for (let i = 0; i < plateMatrix.length; i++) { + const hp = plateMatrix[i] + dummy.position.set(hp.x, hp.y, hp.z) + dummy.rotation.set(hp.rx, hp.ry, hp.rz) + dummy.scale.set(hp.sx, hp.sy, hp.sz) + dummy.updateMatrix() + plateMesh.setMatrixAt(i, dummy.matrix) + } + meshes.push(plateMesh) + } + + if (railwayMatrix.length > 0) { + + if (!this.shuttleRailwayGeometry) { + this.shuttleRailwayGeometry = this.createRailway(railwayMatrix[0].x, railwayMatrix[0].y, railwayMatrix[0].z) + } + if (!this.shuttleRailwayMaterial) { + this.shuttleRailwayMaterial = this.createRailwayMaterial() + } + const dummy = new THREE.Object3D() + const railwayMesh = new THREE.InstancedMesh(this.shuttleRailwayGeometry, this.shuttleRailwayMaterial, railwayMatrix.length) + for (let i = 0; i < railwayMatrix.length; i++) { + const hp = railwayMatrix[i] + dummy.position.set(hp.x, hp.y, hp.z) + dummy.rotation.set(hp.rx, hp.ry, hp.rz) + dummy.scale.set(hp.sx, hp.sy, hp.sz) + dummy.updateMatrix() + railwayMesh.setMatrixAt(i, dummy.matrix) + } + meshes.push(railwayMesh) + } + + return meshes + } + + + resetUVs(geometry: THREE.ExtrudeGeometry) { + if (geometry == undefined) return + const pos = geometry.getAttribute('position'), + nor = geometry.getAttribute('normal'), + uvs = geometry.getAttribute('uv') + + for (let i = 0; i < pos.count; i++) { + let x = 0, y = 0 + + const nx = Math.abs(nor.getX(i)), ny = Math.abs(nor.getY(i)), nz = Math.abs(nor.getZ(i)) + + // if facing X + if (nx >= ny && nx >= nz) { + x = pos.getZ(i) + y = pos.getY(i) + } + + // if facing Y + if (ny >= nx && ny >= nz) { + x = pos.getX(i) + y = pos.getZ(i) + } + + // if facing Z + if (nz >= nx && nz >= ny) { + x = pos.getX(i) + y = pos.getY(i) + } + uvs.setXY(i, x, y) + } + } + +} diff --git a/src/modules/shuttle_rack/index.ts b/src/modules/shuttle_rack/index.ts new file mode 100644 index 0000000..95487dd --- /dev/null +++ b/src/modules/shuttle_rack/index.ts @@ -0,0 +1,15 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import ShuttleRackRenderer from './ShuttleRackRenderer.ts' +import ShuttleRackEntity from './ShuttleRackEntity.ts' +import ShuttleRackInteraction from './ShuttleRackInteraction.ts' +import propertySetter from "./ShuttleRackPropertySetter.ts"; + +export const ITEM_TYPE_NAME = 'shuttle_rack' + +export default defineModule({ + name: ITEM_TYPE_NAME, + renderer: new ShuttleRackRenderer(ITEM_TYPE_NAME), + interaction: new ShuttleRackInteraction(ITEM_TYPE_NAME), + setter: propertySetter, + entity: ShuttleRackEntity, +})