From 658f4136e99e74cdf95f32e3b5af497e9d94bcb6 Mon Sep 17 00:00:00 2001 From: yuliang <398780299@qq.com> Date: Fri, 6 Jun 2025 11:15:59 +0800 Subject: [PATCH] =?UTF-8?q?ptr=202D=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/images/ptr/ptr.png | Bin 0 -> 4276 bytes src/core/manager/WorldModel.ts | 6 +- src/example/example1.js | 44 +++++++++--- src/modules/gstore/GstoreRenderer.ts | 42 ++++++------ src/modules/pallet/PalletEntity.ts | 5 ++ src/modules/pallet/PalletInteraction.ts | 22 ++++++ src/modules/pallet/PalletMeta.ts | 49 ++++++++++++++ src/modules/pallet/PalletRenderer.ts | 115 ++++++++++++++++++++++++++++++++ src/modules/pallet/index.ts | 15 +++++ src/modules/ptr/PtrEntity.ts | 5 ++ src/modules/ptr/PtrInteraction.ts | 22 ++++++ src/modules/ptr/PtrMeta.ts | 49 ++++++++++++++ src/modules/ptr/PtrRenderer.ts | 98 +++++++++++++++++++++++++++ src/modules/ptr/index.ts | 15 +++++ src/modules/rack/RackInteraction.ts | 4 +- src/modules/rack/RackRenderer.ts | 8 +-- 16 files changed, 463 insertions(+), 36 deletions(-) create mode 100644 src/assets/images/ptr/ptr.png create mode 100644 src/modules/pallet/PalletEntity.ts create mode 100644 src/modules/pallet/PalletInteraction.ts create mode 100644 src/modules/pallet/PalletMeta.ts create mode 100644 src/modules/pallet/PalletRenderer.ts create mode 100644 src/modules/pallet/index.ts create mode 100644 src/modules/ptr/PtrEntity.ts create mode 100644 src/modules/ptr/PtrInteraction.ts create mode 100644 src/modules/ptr/PtrMeta.ts create mode 100644 src/modules/ptr/PtrRenderer.ts create mode 100644 src/modules/ptr/index.ts diff --git a/src/assets/images/ptr/ptr.png b/src/assets/images/ptr/ptr.png new file mode 100644 index 0000000000000000000000000000000000000000..b65c9570950389503e783cb79098f8ebce66fdb1 GIT binary patch literal 4276 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 W_v=lsR$89`0000 { console.log('世界模型初始化完成') diff --git a/src/example/example1.js b/src/example/example1.js index 746bb0e..9202110 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -83,7 +83,7 @@ export default { tf: [[-1, 0.1, 3.5], [0, 0, 0], [1.5, 0.25, 0.1]], dt: { rackDepth: 1, // 货架深度 - rackWidth: 8, + rackWidth: 7, rackHeight: 4.2, levelCount: 3, // 总层数 bayCount: 5, // 总列数 @@ -92,13 +92,13 @@ export default { columnSpacing: 1, // 支脚跨越 bays: [ // 每列的配置 { - bayWidth: 1.6, // 列的宽度 + bayWidth: 1.4, // 列的宽度 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 ]}, + {bayWidth: 1.4, levelHeight: [ 1.4, 1.4, 1.4 ]}, + {bayWidth: 1.4, levelHeight: [ 1.4, 1.4, 1.4 ]}, + {bayWidth: 1.4, levelHeight: [ 1.4, 1.4, 1.4 ]}, + {bayWidth: 1.4, levelHeight: [ 1.4, 1.4, 1.4 ]}, ] } }, @@ -136,14 +136,38 @@ export default { id: '6UhIIw9QPYh6acwyW8OSGs', t: 'gstore', v: true, - tf: [[-1, 0.1, 0.75], [0, 0, 0], [1.5, 1.2, 0.1]], - dt: { in: [], out: [], center: [], storeWidth: 1.2, storeDepth: 1.2 } + tf: [[-1, 0.1, 0.55], [0, 0, 0], [1.5, 1.2, 0.1]], + dt: { in: [], out: [], center: [], storeWidth: 1.4, storeDepth: 1.4 } }, { id: '1D0WSRPj8JJJwIcmA0UMqG', t: 'gstore', v: true, - tf: [[0.75, 0.1, 0.75], [0, 0, 0], [1.5, 1.2, 0.1]], - dt: { in: [], out: [], center: [], storeWidth: 1.2, storeDepth: 1.2 } + tf: [[0.75, 0.1, 0.55], [0, 0, 0], [1.5, 1.2, 0.1]], + dt: { in: [], out: [], center: [], storeWidth: 1.4, storeDepth: 1.4 } + }, { + id: 'pallet1', + t: 'pallet', + v: true, + tf: [[0.75, 0.075, 0.55], [0, 0, 0], [1.5, 1.2, 0.1]], + dt: { in: [], out: [], center: [], palletWidth: 1, palletDepth: 1.2 } + }, { + id: 'pallet2', + t: 'pallet', + v: true, + tf: [[3, 0.075, 0.55], [0, 0, 0], [1.5, 1.2, 0.1]], + dt: { in: [], out: [], center: [], palletWidth: 1, palletDepth: 1.2 } + }, { + id: 'ptr1', + t: 'ptr', + v: true, + tf: [[0.75, 0.075, 2], [0, 0, 0], [1.5, 1.2, 0.1]], + dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 } + }, { + id: 'ptr2', + t: 'ptr', + v: true, + tf: [[3, 0.075, 2], [0, 0, 0], [1.5, 1.2, 0.1]], + dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 } } ] } diff --git a/src/modules/gstore/GstoreRenderer.ts b/src/modules/gstore/GstoreRenderer.ts index 96785d6..2f7ea83 100644 --- a/src/modules/gstore/GstoreRenderer.ts +++ b/src/modules/gstore/GstoreRenderer.ts @@ -33,7 +33,7 @@ export default class GstoreRenderer extends BaseRenderer { const point = objects[0] point.position.y = this.defulePositionY - point.scale.set(item.dt.storeWidth, this.defaultScale.y, item.dt.storeDepth) + // point.scale.set(item.dt.storeWidth, this.defaultScale.y, item.dt.storeDepth) point.rotation.set( THREE.MathUtils.degToRad(this.defaultRotation.x), THREE.MathUtils.degToRad(this.defaultRotation.y), @@ -50,44 +50,41 @@ export default class GstoreRenderer extends BaseRenderer { throw new Error('not allow store line.') } - createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { + createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { // 创建平面几何体 - + if (!item.dt.storeWidth || !item.dt.storeDepth) { + return [] + } const group = new THREE.Group() group.name = GstoreRenderer.POINT_NAME // 绘制背景矩形框 - const planeGeometry = new THREE.PlaneGeometry(1, 1); - planeGeometry.rotateX(-Math.PI / 2) + const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth); + planeGeometry.rotateX(Math.PI / 2) const planeMaterial = new THREE.MeshBasicMaterial({ - color: 'white', + color: '#dee8ee', transparent: true, // 启用透明 - opacity: 0.2, // 50%透明度 + opacity: 0.5, // 50%透明度 depthWrite: false, // 防止深度冲突 side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} }); const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); group.add(planeMesh) - if (!item.dt.storeWidth || !item.dt.storeDepth) { - return [group] - } - // 绘制边框 const lineXLen = item.dt.storeWidth - this.defaultLineWidth const lineYLen = item.dt.storeDepth - this.defaultLineWidth const lineGeometry = new LineGeometry().setPositions([ - -(lineXLen/2),-(lineYLen/2),0, - lineXLen/2,-(lineYLen/2),0, - lineXLen/2,lineYLen/2,0, - -(lineXLen/2),lineYLen/2,0, - -(lineXLen/2),-(lineYLen/2),0 + -(lineXLen/2),0,-(lineYLen/2), + lineXLen/2,0,-(lineYLen/2), + lineXLen/2,0,lineYLen/2, + -(lineXLen/2),0,lineYLen/2, + -(lineXLen/2),0,-(lineYLen/2) ]); - lineGeometry.rotateX(-Math.PI / 2) const lineMaterial = new LineMaterial({ color: '#038217', - linewidth: 0.05, + linewidth: this.defaultLineWidth, worldUnits: true, resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), side: THREE.DoubleSide @@ -96,7 +93,14 @@ export default class GstoreRenderer extends BaseRenderer { const line = new Line2(lineGeometry, lineMaterial); group.add(line as THREE.Object3D) - return [group] + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + const points = [group] + this.fillObjectUserDataFromItem(item, ...points) + this.afterCreateOrUpdatePoint(item, option, points) + this.tempViewport.entityManager.appendObject(item.id, points) + this.appendToScene(...points) + return points } dispose() { diff --git a/src/modules/pallet/PalletEntity.ts b/src/modules/pallet/PalletEntity.ts new file mode 100644 index 0000000..9cf671f --- /dev/null +++ b/src/modules/pallet/PalletEntity.ts @@ -0,0 +1,5 @@ +import BaseEntity from '@/core/base/BaseItemEntity.ts' + +export default class PalletEntity extends BaseEntity { + +} diff --git a/src/modules/pallet/PalletInteraction.ts b/src/modules/pallet/PalletInteraction.ts new file mode 100644 index 0000000..c01c91b --- /dev/null +++ b/src/modules/pallet/PalletInteraction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class PalletInteraction 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/pallet/PalletMeta.ts b/src/modules/pallet/PalletMeta.ts new file mode 100644 index 0000000..d39f5d0 --- /dev/null +++ b/src/modules/pallet/PalletMeta.ts @@ -0,0 +1,49 @@ +import type { IMeta } from '@/core/base/IMeta.ts' + +export default [ + { field: 'uuid', editor: 'UUID', label: 'uuid', readonly: true, category: 'basic' }, + { field: 'name', editor: 'TextInput', label: '名称', category: 'basic' }, + { field: 'dt.label', editor: 'TextInput', label: '标签', category: 'basic' }, + { editor: 'TransformEditor', category: 'basic' }, + { field: 'dt.color', editor: 'Color', label: '颜色', category: 'basic' }, + { editor: '-', category: 'basic' }, + + { field: 'dt.palletWidth', editor: 'NumberInput', label: '托盘深度', category: 'basic' }, + { field: 'dt.palletDepth', editor: 'NumberInput', label: '托盘深度', category: 'basic' }, + /** + * dt.bays 5列3层货架示例 + * { + * dt: { + * rackDepth: 1.1, // 货架深度 + * levelCount: 3, // 总层数 + * bayCount: 5, // 总列数 + * hideFloor: false, // 隐藏底板 + * extendColumns: true, // 扩展挡板 + * columnSpacing: 1, // 支脚跨越 + * bays: [ // 每列的配置 + * { + * 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 ]}, + * {bayWidth: 1.6, levelHeight: [ 1.4, 1.4, 1.4 ]}, + * ] + * } + * } + * + * + * + * + * + * + * + */ + + + { field: 'tf', editor: 'InOutCenterEditor', category: 'basic' }, + { field: 'dt.selectable', editor: 'Switch', label: '可选中', category: 'basic' }, + { field: 'dt.protected', editor: 'Switch', label: '受保护', category: 'basic' }, + { field: 'visible', editor: 'Switch', label: '可见', category: 'basic' } +] as IMeta diff --git a/src/modules/pallet/PalletRenderer.ts b/src/modules/pallet/PalletRenderer.ts new file mode 100644 index 0000000..c1ca858 --- /dev/null +++ b/src/modules/pallet/PalletRenderer.ts @@ -0,0 +1,115 @@ +import * as THREE from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import { Line2 } from 'three/examples/jsm/lines/Line2.js' +import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js' +import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js' +import {decimalSumBy} from "@/core/ModelUtils"; +import Constract from '@/core/Constract.ts' + +/** + * 货架货位渲染器 + */ +export default class PalletRenderer extends BaseRenderer { + static POINT_NAME = 'pallet' + + pointMaterial: THREE.Material + + /** + * 默认点的高度, 防止和地面重合 + */ + readonly defulePositionY: number = Constract.HEIGHT_WAY + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1, 1, 1) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + readonly defaultLineWidth: number = 0.15 + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + /** + * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 + */ + override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, objects: THREE.Object3D[]) { + super.afterCreateOrUpdatePoint(item, option, objects) + + const point = objects[0] + // 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(this.defaultRotation.x), + THREE.MathUtils.degToRad(this.defaultRotation.y), + THREE.MathUtils.degToRad(this.defaultRotation.z) + ) + } + + + 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.palletWidth || !item.dt.palletDepth) { + return [] + } + + const group = new THREE.Group() + group.name = PalletRenderer.POINT_NAME + + // 绘制背景矩形框 + const planeGeometry = new THREE.PlaneGeometry(item.dt.palletWidth - this.defaultLineWidth, item.dt.palletDepth - this.defaultLineWidth); + planeGeometry.rotateX(Math.PI / 2) + const planeMaterial = new THREE.MeshBasicMaterial({ + color: '#029de5', + side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} + }); + const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); + group.add(planeMesh) + + // 绘制边框 + const lineXLen = item.dt.palletWidth - this.defaultLineWidth + const lineYLen = item.dt.palletDepth - this.defaultLineWidth + + const lineGeometry = new LineGeometry().setPositions([ + -(lineXLen/2),0,-(lineYLen/2), + lineXLen/2,0,-(lineYLen/2), + lineXLen/2,0,lineYLen/2, + -(lineXLen/2),0,lineYLen/2, + -(lineXLen/2),0,-(lineYLen/2) + ]); + const lineMaterial = new LineMaterial({ + color: '#029de5', + linewidth: this.defaultLineWidth, + worldUnits: true, + resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), + side: THREE.DoubleSide + }); + // + const line = new Line2(lineGeometry, lineMaterial); + group.add(line as THREE.Object3D) + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + + const points = [group] + this.fillObjectUserDataFromItem(item, ...points) + this.afterCreateOrUpdatePoint(item, option, points) + this.tempViewport.entityManager.appendObject(item.id, points) + this.appendToScene(...points) + return points + } + + dispose() { + super.dispose() + this.pointMaterial?.dispose() + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): Object3D[] { + return []; + } +} diff --git a/src/modules/pallet/index.ts b/src/modules/pallet/index.ts new file mode 100644 index 0000000..d49b5d9 --- /dev/null +++ b/src/modules/pallet/index.ts @@ -0,0 +1,15 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import PalletRenderer from './PalletRenderer.ts' +import PalletEntity from './PalletEntity.ts' +import PalletMeta from './PalletMeta.ts' +import PalletInteraction from './PalletInteraction.ts' + +export const ITEM_TYPE_NAME = 'pallet' + +export default defineModule({ + name: ITEM_TYPE_NAME, + renderer: new PalletRenderer(ITEM_TYPE_NAME), + interaction: new PalletInteraction(ITEM_TYPE_NAME), + meta: PalletMeta, + entity: PalletEntity +}) diff --git a/src/modules/ptr/PtrEntity.ts b/src/modules/ptr/PtrEntity.ts new file mode 100644 index 0000000..468396a --- /dev/null +++ b/src/modules/ptr/PtrEntity.ts @@ -0,0 +1,5 @@ +import BaseEntity from '@/core/base/BaseItemEntity.ts' + +export default class PtrEntity extends BaseEntity { + +} diff --git a/src/modules/ptr/PtrInteraction.ts b/src/modules/ptr/PtrInteraction.ts new file mode 100644 index 0000000..39ac680 --- /dev/null +++ b/src/modules/ptr/PtrInteraction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class PtrInteraction extends BaseInteraction { + + get isSinglePointMode(): boolean { + return true + } + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + createPointOfItem(item: ItemJson, point: THREE.Vector3): ItemJson { + item = super.createPointOfItem(item, point) + + // 创建一个地堆货架 + item.dt.palletWidth = 1 // 宽度 + item.dt.palletDepth = 1.2 // 深度 + return item + } +} diff --git a/src/modules/ptr/PtrMeta.ts b/src/modules/ptr/PtrMeta.ts new file mode 100644 index 0000000..d39f5d0 --- /dev/null +++ b/src/modules/ptr/PtrMeta.ts @@ -0,0 +1,49 @@ +import type { IMeta } from '@/core/base/IMeta.ts' + +export default [ + { field: 'uuid', editor: 'UUID', label: 'uuid', readonly: true, category: 'basic' }, + { field: 'name', editor: 'TextInput', label: '名称', category: 'basic' }, + { field: 'dt.label', editor: 'TextInput', label: '标签', category: 'basic' }, + { editor: 'TransformEditor', category: 'basic' }, + { field: 'dt.color', editor: 'Color', label: '颜色', category: 'basic' }, + { editor: '-', category: 'basic' }, + + { field: 'dt.palletWidth', editor: 'NumberInput', label: '托盘深度', category: 'basic' }, + { field: 'dt.palletDepth', editor: 'NumberInput', label: '托盘深度', category: 'basic' }, + /** + * dt.bays 5列3层货架示例 + * { + * dt: { + * rackDepth: 1.1, // 货架深度 + * levelCount: 3, // 总层数 + * bayCount: 5, // 总列数 + * hideFloor: false, // 隐藏底板 + * extendColumns: true, // 扩展挡板 + * columnSpacing: 1, // 支脚跨越 + * bays: [ // 每列的配置 + * { + * 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 ]}, + * {bayWidth: 1.6, levelHeight: [ 1.4, 1.4, 1.4 ]}, + * ] + * } + * } + * + * + * + * + * + * + * + */ + + + { field: 'tf', editor: 'InOutCenterEditor', category: 'basic' }, + { field: 'dt.selectable', editor: 'Switch', label: '可选中', category: 'basic' }, + { field: 'dt.protected', editor: 'Switch', label: '受保护', category: 'basic' }, + { field: 'visible', editor: 'Switch', label: '可见', category: 'basic' } +] as IMeta diff --git a/src/modules/ptr/PtrRenderer.ts b/src/modules/ptr/PtrRenderer.ts new file mode 100644 index 0000000..3de33b7 --- /dev/null +++ b/src/modules/ptr/PtrRenderer.ts @@ -0,0 +1,98 @@ +import * as THREE from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import { Line2 } from 'three/examples/jsm/lines/Line2.js' +import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js' +import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js' +import {decimalSumBy} from "@/core/ModelUtils"; +import Constract from '@/core/Constract.ts' +import ptrUrl from '@/assets/images/ptr/ptr.png' + +/** + * ptr侧叉渲染器 + */ +export default class PtrRenderer extends BaseRenderer { + static POINT_NAME = 'ptr' + + pointMaterial: THREE.Material + + /** + * 默认点的高度, 防止和地面重合 + */ + readonly defulePositionY: number = Constract.HEIGHT_WAY + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1, 1, 1) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + readonly defaultLineWidth: number = 0.15 + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + /** + * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 + */ + override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, objects: THREE.Object3D[]) { + super.afterCreateOrUpdatePoint(item, option, objects) + + const point = objects[0] + // 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(this.defaultRotation.x), + THREE.MathUtils.degToRad(this.defaultRotation.y), + THREE.MathUtils.degToRad(this.defaultRotation.z) + ) + } + + + createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D[] { + throw new Error('not allow store line.') + } + + updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { + throw new Error('not allow store line.') + } + + + createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { + // 创建平面几何体 + if (!item.dt.ptrWidth || !item.dt.ptrDepth) { + return [] + } + + const textureLoader = new THREE.TextureLoader() + const texture = textureLoader.load(ptrUrl) + + const group = new THREE.Group() + group.name = PtrRenderer.POINT_NAME + + // 绘制背景矩形框 + const planeGeometry = new THREE.PlaneGeometry(item.dt.ptrWidth, item.dt.ptrDepth); + planeGeometry.rotateX(-Math.PI / 2) + planeGeometry.rotateY(-Math.PI / 2) + const planeMaterial = new THREE.MeshLambertMaterial({ + map: texture, // 颜色贴图 + transparent: true, // 允许透明纹理 + }); + const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); + group.add(planeMesh) + + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + + const points = [group] + this.fillObjectUserDataFromItem(item, ...points) + this.afterCreateOrUpdatePoint(item, option, points) + this.tempViewport.entityManager.appendObject(item.id, points) + this.appendToScene(...points) + return points + } + + dispose() { + super.dispose() + this.pointMaterial?.dispose() + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): Object3D[] { + return []; + } +} diff --git a/src/modules/ptr/index.ts b/src/modules/ptr/index.ts new file mode 100644 index 0000000..7627f9b --- /dev/null +++ b/src/modules/ptr/index.ts @@ -0,0 +1,15 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import PtrRenderer from './PtrRenderer.ts' +import PtrEntity from './PtrEntity.ts' +import PtrMeta from './PtrMeta.ts' +import PtrInteraction from './PtrInteraction.ts' + +export const ITEM_TYPE_NAME = 'ptr' + +export default defineModule({ + name: ITEM_TYPE_NAME, + renderer: new PtrRenderer(ITEM_TYPE_NAME), + interaction: new PtrInteraction(ITEM_TYPE_NAME), + meta: PtrMeta, + entity: PtrEntity +}) diff --git a/src/modules/rack/RackInteraction.ts b/src/modules/rack/RackInteraction.ts index 7a7b41d..93c0262 100644 --- a/src/modules/rack/RackInteraction.ts +++ b/src/modules/rack/RackInteraction.ts @@ -15,8 +15,8 @@ export default class RackInteraction extends BaseInteraction { item = super.createPointOfItem(item, point) // 创建一个地堆货架 - item.dt.storeWidth = 1.2 // 宽度 - item.dt.storeDepth = 1.2 // 深度 + item.dt.rackWidth = 3 // 宽度 + item.dt.rackDepth = 1 // 深度 return item } } diff --git a/src/modules/rack/RackRenderer.ts b/src/modules/rack/RackRenderer.ts index 9089001..8701a83 100644 --- a/src/modules/rack/RackRenderer.ts +++ b/src/modules/rack/RackRenderer.ts @@ -55,6 +55,9 @@ export default class RackRenderer extends BaseRenderer { createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { // 创建平面几何体 + if (!item.dt.bays || !item.dt.rackDepth) { + return [] + } const group = new THREE.Group() group.name = RackRenderer.POINT_NAME @@ -82,9 +85,6 @@ export default class RackRenderer extends BaseRenderer { const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial) group.add(planeMesh) - if (!item.dt.bays || !item.dt.rackDepth) { - return [group] - } // 绘制边框 const lineXLen = rackWidth - this.defaultLineWidth @@ -99,7 +99,7 @@ export default class RackRenderer extends BaseRenderer { ]) const lineMaterial = new LineMaterial({ color: '#0d89a5', - linewidth: 0.05, + linewidth: this.defaultLineWidth, worldUnits: true, resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), side: THREE.DoubleSide