From b073dbfa75803e77f9d63d8fcfcc9c69d7265735 Mon Sep 17 00:00:00 2001 From: yuliang <398780299@qq.com> Date: Wed, 4 Jun 2025 17:55:14 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=9C=B0=E5=A0=86=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Model3DView.vue | 10 ++++++ src/modules/gstore/GstoreRenderer.ts | 63 ++++++++++++++++++++++++++++++------ 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/components/Model3DView.vue b/src/components/Model3DView.vue index e5b7ecf..110e9db 100644 --- a/src/components/Model3DView.vue +++ b/src/components/Model3DView.vue @@ -17,6 +17,7 @@ 添加输送线 添加货架 + 添加地堆
材质颜色 @@ -378,6 +379,15 @@ function createShelf(){//创建货架 }) } +function createGroundStore() { + const planeGeometry = new THREE.PlaneGeometry(1, 1); + const material = new THREE.MeshBasicMaterial({ + color: 0x00ff00, + side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} + }); + const planeMesh = new THREE.Mesh(planeGeometry, material); + scene.add(planeMesh); +} function initThree() { const viewerDom = canvasContainer.value diff --git a/src/modules/gstore/GstoreRenderer.ts b/src/modules/gstore/GstoreRenderer.ts index 789b311..96fcc95 100644 --- a/src/modules/gstore/GstoreRenderer.ts +++ b/src/modules/gstore/GstoreRenderer.ts @@ -1,15 +1,14 @@ import * as THREE from 'three' import BaseRenderer from '@/core/base/BaseRenderer.ts' -import { Text } from 'troika-three-text' -import MoveLinePointPng from '@/assets/images/moveline_point.png' -import SimSunTTF from '@/assets/fonts/simsunb.ttf' -import { getLineId } from '@/core/ModelUtils.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' /** - * 辅助测量工具渲染器 + * 地堆货位渲染器 */ export default class GstoreRenderer extends BaseRenderer { - static POINT_NAME = 'way_point' + static POINT_NAME = 'ground_store' pointMaterial: THREE.Material @@ -19,6 +18,7 @@ export default class GstoreRenderer extends BaseRenderer { readonly defulePositionY: number = 0.5 // 默认点的高度, 0.01, 防止和地面重合 readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1.5, 1.2, 0.1) readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + readonly defaultLineWidth: number = 0.05 constructor(itemTypeName: string) { super(itemTypeName) @@ -50,13 +50,56 @@ export default class GstoreRenderer extends BaseRenderer { } createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { - const obj = new THREE.Sprite(this.pointMaterial as THREE.SpriteMaterial) - obj.name = GstoreRenderer.POINT_NAME - return [obj] + // 创建平面几何体 + + const group = new THREE.Group() + group.name = GstoreRenderer.POINT_NAME + + // 绘制背景矩形框 + const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth); + planeGeometry.rotateX(-Math.PI / 2) + const planeMaterial = new THREE.MeshBasicMaterial({ + color: 'white', + transparent: true, // 启用透明 + opacity: 0.2, // 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 + ]); + lineGeometry.rotateX(-Math.PI / 2) + const lineMaterial = new LineMaterial({ + color: 0x00ff00, + linewidth: 0.05, + 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) + + return [group] } dispose() { super.dispose() this.pointMaterial.dispose() } -} \ No newline at end of file +} From 0334750c0555c512a194b64d14b5e9b7586d13bf Mon Sep 17 00:00:00 2001 From: luoyifan Date: Wed, 4 Jun 2025 18:02:39 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=8F=AA=E6=9C=89=E8=A2=AB=E9=80=89?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=85=83=E7=B4=A0=E6=89=8D=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E6=8B=96=E6=8B=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/base/BaseRenderer.ts | 5 +- src/core/controls/SelectInspect.ts | 34 +++++++ src/core/manager/EntityManager.ts | 169 ++++++++++++++++++++------------- src/modules/measure/MeasureRenderer.ts | 1 - 4 files changed, 139 insertions(+), 70 deletions(-) diff --git a/src/core/base/BaseRenderer.ts b/src/core/base/BaseRenderer.ts index cc279e8..e65a077 100644 --- a/src/core/base/BaseRenderer.ts +++ b/src/core/base/BaseRenderer.ts @@ -116,9 +116,8 @@ export default abstract class BaseRenderer { console.warn('No active viewport to append objects to.') return } - - const dragObjects = objects.filter(obj => !!obj.userData.draggable) - this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') + // const dragObjects = objects.filter(obj => !!obj.userData.draggable) + // this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') this.tempViewport.scene.add(...objects) } diff --git a/src/core/controls/SelectInspect.ts b/src/core/controls/SelectInspect.ts index 1d07855..f2def9f 100644 --- a/src/core/controls/SelectInspect.ts +++ b/src/core/controls/SelectInspect.ts @@ -5,6 +5,9 @@ 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 EventBus from '@/runtime/EventBus' +import { markRaw } from 'vue' +import { getMeta } from '@/core/manager/ModuleManager.ts' +import MouseMoveInspect from '@/core/controls/MouseMoveInspect.ts' let pdFn, pmFn, puFn @@ -52,6 +55,8 @@ export default class SelectInspect implements IControls { */ selectionId: string + clickTime: number | null = null + constructor() { } @@ -196,6 +201,9 @@ export default class SelectInspect implements IControls { // 记录鼠标按下位置 this.recStartPos = this.viewport.getClosestIntersection(event) this.createRectangle() + } else { + // 为 click 事件添加处理逻辑 + this.clickTime = Date.now() } } @@ -221,5 +229,31 @@ export default class SelectInspect implements IControls { onMouseUp(event: MouseEvent) { this.disposeRect() + const clickTime = this.clickTime + this.clickTime = null + if (Date.now() - clickTime < 200) { + // 如果是点击事件,触发选中逻辑 + const objects: THREE.Object3D[] = this.viewport.entityManager.getObjectByCanvasMouse(event) + if (objects.length > 0) { + const object = objects[0] + const entityId = object.userData.entityId + const item = this.viewport.entityManager.findItemById(entityId) + const itemTypeName = object.userData.t + if (item.dt.protected !== true) { + this.viewport.state.selectedObject = markRaw(object) + this.viewport.state.selectedItem = markRaw(item) + this.viewport.state.selectedEntityId = entityId + this.viewport.state.selectedObjectMeta = getMeta(itemTypeName) + + EventBus.dispatch('selectedObjectChanged', { + viewport: markRaw(this.viewport), + selectedObject: this.viewport.state.selectedObject, + selectedItem: this.viewport.state.selectedItem, + selectedEntityId: this.viewport.state.selectedEntityId, + selectedObjectMeta: this.viewport.state.selectedObjectMeta + }) + } + } + } } } diff --git a/src/core/manager/EntityManager.ts b/src/core/manager/EntityManager.ts index 9af1f09..a09fc19 100644 --- a/src/core/manager/EntityManager.ts +++ b/src/core/manager/EntityManager.ts @@ -3,6 +3,7 @@ import type Viewport from '@/core/engine/Viewport' import type BaseRenderer from '@/core/base/BaseRenderer' import { getRenderer } from './ModuleManager' import { getLineId, parseLineId } from '@/core/ModelUtils' +import { Vector2 } from 'three' /** * 实体管理器 @@ -24,6 +25,9 @@ export default class EntityManager { // 所有 THREEJS "点"对象, 检索值是"点实体"的 id, 值是 THREE.Object3D 数组 private readonly objects = new Map() + // 所有 THREEJS "可选中"对象, 检索值是"点实体"的 id, 值是 THREE.Object3D 数组 + private readonly _selectableObjects: THREE.Object3D[] = [] + // 所有 THREEJS "线"对象, 检索值是"线实体"的 id, 取值方式是 {type}${startId}${endId}, 值是 THREE.Object3D 数组 private readonly lines = new Map() @@ -404,70 +408,70 @@ export default class EntityManager { } - /** - * 重命名一个点 - * 注意, 不能在更新时刻改名. 所有的关系节点都应该改名 - */ - renamePoint(newId: string, originId: string) { - if (this.isUpdating) { - throw new Error('Cannot rename point during update') - } - const entity = this.entities.get(originId) - if (!entity) { - throw new Error(`Entity with id ${originId} does not exist`) - } - if (this.entities.has(newId)) { - throw new Error(`Entity with id ${newId} already exists`) - } - entity.id = newId - this.entities.set(newId, entity) - this.entities.delete(originId) - this.objects.set(newId, this.objects.get(originId) || []) - this.objects.delete(originId) - - // 更新关系索引 - const relations = this.relationIndex.get(originId) - if (relations) { - this.relationIndex.delete(originId) - - // 更新所有关系中的 id - relations.center.forEach((relatedId) => { - const rev = this.relationIndex.get(relatedId) - if (rev && rev.delete('center', originId)) { - rev.add('center', newId) - } - }) - relations.input.forEach((relatedId) => { - const rev = this.relationIndex.get(relatedId) - if (rev && rev.delete('out', originId)) { - rev.add('out', newId) - } - }) - relations.output.forEach((relatedId) => { - const rev = this.relationIndex.get(relatedId) - if (rev && rev.delete('in', originId)) { - rev.add('in', newId) - } - }) - - this.relationIndex.set(newId, relations) - } - - // 更新所有线段数据 - for (const [lineId, lineObjects] of this.lines.entries()) { - const [type, startId, endId] = parseLineId(lineId) - if (startId === originId) { - const newLineId = getLineId(newId, endId, type) - this.lines.set(newLineId, lineObjects) - this.lines.delete(lineId) - - } else if (endId === originId) { - const newLineId = getLineId(startId, newId, type) - this.lines.set(newLineId, lineObjects) - this.lines.delete(lineId) - } - } - } + // /** + // * 重命名一个点 + // * 注意, 不能在更新时刻改名. 所有的关系节点都应该改名 + // */ + // renamePoint(newId: string, originId: string) { + // if (this.isUpdating) { + // throw new Error('Cannot rename point during update') + // } + // const entity = this.entities.get(originId) + // if (!entity) { + // throw new Error(`Entity with id ${originId} does not exist`) + // } + // if (this.entities.has(newId)) { + // throw new Error(`Entity with id ${newId} already exists`) + // } + // entity.id = newId + // this.entities.set(newId, entity) + // this.entities.delete(originId) + // this.objects.set(newId, this.objects.get(originId) || []) + // this.objects.delete(originId) + // + // // 更新关系索引 + // const relations = this.relationIndex.get(originId) + // if (relations) { + // this.relationIndex.delete(originId) + // + // // 更新所有关系中的 id + // relations.center.forEach((relatedId) => { + // const rev = this.relationIndex.get(relatedId) + // if (rev && rev.delete('center', originId)) { + // rev.add('center', newId) + // } + // }) + // relations.input.forEach((relatedId) => { + // const rev = this.relationIndex.get(relatedId) + // if (rev && rev.delete('out', originId)) { + // rev.add('out', newId) + // } + // }) + // relations.output.forEach((relatedId) => { + // const rev = this.relationIndex.get(relatedId) + // if (rev && rev.delete('in', originId)) { + // rev.add('in', newId) + // } + // }) + // + // this.relationIndex.set(newId, relations) + // } + // + // // 更新所有线段数据 + // for (const [lineId, lineObjects] of this.lines.entries()) { + // const [type, startId, endId] = parseLineId(lineId) + // if (startId === originId) { + // const newLineId = getLineId(newId, endId, type) + // this.lines.set(newLineId, lineObjects) + // this.lines.delete(lineId) + // + // } else if (endId === originId) { + // const newLineId = getLineId(startId, newId, type) + // this.lines.set(newLineId, lineObjects) + // this.lines.delete(lineId) + // } + // } + // } deleteEntityOnly(id: string) { return this.entities.delete(id) @@ -478,15 +482,28 @@ export default class EntityManager { } deleteObjectsOnly(id: string) { + // 删除对象时,也需要从 _selectableObjects 中移除 + const rel = this.objects.get(id) + if (rel) { + _.remove(this._selectableObjects, obj => !rel.includes(obj)) + } return this.objects.delete(id) } appendObject(id: string, points: THREE.Object3D[]) { this.objects.set(id, points) + // 如果是可选中对象,添加到 _selectableObjects 中 + if (points.some(obj => obj.userData.selectable !== false)) { + this._selectableObjects.push(...points) + } } appendLineObject(id: string, lines: THREE.Object3D[]) { this.lines.set(id, lines) + // 如果是可选中对象,添加到 _selectableObjects 中 + if (lines.some(obj => obj.userData.selectable !== false)) { + this._selectableObjects.push(...lines) + } } findLineObjectsById(lineId: string): THREE.Object3D[] { @@ -494,6 +511,11 @@ export default class EntityManager { } deleteLineObjectOnly(id: string) { + // 删除线对象时,也需要从 _selectableObjects 中移除 + const rel = this.lines.get(id) + if (rel) { + _.remove(this._selectableObjects, obj => !rel.includes(obj)) + } return this.lines.delete(id) } @@ -507,8 +529,23 @@ export default class EntityManager { return this.entities.get(linkStartPointId) } - getEntityMap() { - return this.entities + getObjectByCanvasMouse(event: MouseEvent): THREE.Object3D[] { + const _domElement = this.viewport.renderer.domElement + const rect = _domElement.getBoundingClientRect() + const _pointer = new Vector2() + _pointer.x = (event.clientX - rect.left) / rect.width * 2 - 1 + _pointer.y = -(event.clientY - rect.top) / rect.height * 2 + 1 + this.viewport.raycaster.setFromCamera(_pointer, this.viewport.camera) + const _intersections = this.viewport.raycaster.intersectObjects(this._selectableObjects, true) + + if (!_intersections || _intersections.length === 0) { + return [] + } + // 根据距离排序射线命中的对象集 + return _.map( + _intersections.sort((a, b) => a.distance - b.distance), + r => r.object + ) } } diff --git a/src/modules/measure/MeasureRenderer.ts b/src/modules/measure/MeasureRenderer.ts index f4f01b9..610e214 100644 --- a/src/modules/measure/MeasureRenderer.ts +++ b/src/modules/measure/MeasureRenderer.ts @@ -110,7 +110,6 @@ export default class MeasureRenderer extends BaseRenderer { const dragObjects = objects.filter(obj => !!obj.userData.draggable) this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') - // this.tempViewport.dragControl.setDragObjects(objects, 'remove') this.group.add(...objects) } From a1be15ad468cd1f124c2dafd0a5156a4b463fece Mon Sep 17 00:00:00 2001 From: yuliang <398780299@qq.com> Date: Wed, 4 Jun 2025 18:07:51 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=96=B0=E5=BB=BARack=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/rack/RackEntity.ts | 5 ++ src/modules/rack/RackInteraction.ts | 22 ++++++++ src/modules/rack/RackMeta.ts | 14 +++++ src/modules/rack/RackRenderer.ts | 105 ++++++++++++++++++++++++++++++++++++ src/modules/rack/index.ts | 15 ++++++ 5 files changed, 161 insertions(+) create mode 100644 src/modules/rack/RackEntity.ts create mode 100644 src/modules/rack/RackInteraction.ts create mode 100644 src/modules/rack/RackMeta.ts create mode 100644 src/modules/rack/RackRenderer.ts create mode 100644 src/modules/rack/index.ts diff --git a/src/modules/rack/RackEntity.ts b/src/modules/rack/RackEntity.ts new file mode 100644 index 0000000..713ed0e --- /dev/null +++ b/src/modules/rack/RackEntity.ts @@ -0,0 +1,5 @@ +import BaseEntity from '@/core/base/BaseItemEntity.ts' + +export default class RackEntity extends BaseEntity { + +} diff --git a/src/modules/rack/RackInteraction.ts b/src/modules/rack/RackInteraction.ts new file mode 100644 index 0000000..7a7b41d --- /dev/null +++ b/src/modules/rack/RackInteraction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class RackInteraction 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.storeWidth = 1.2 // 宽度 + item.dt.storeDepth = 1.2 // 深度 + return item + } +} diff --git a/src/modules/rack/RackMeta.ts b/src/modules/rack/RackMeta.ts new file mode 100644 index 0000000..2273048 --- /dev/null +++ b/src/modules/rack/RackMeta.ts @@ -0,0 +1,14 @@ +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: '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 \ No newline at end of file diff --git a/src/modules/rack/RackRenderer.ts b/src/modules/rack/RackRenderer.ts new file mode 100644 index 0000000..e6d5984 --- /dev/null +++ b/src/modules/rack/RackRenderer.ts @@ -0,0 +1,105 @@ +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' + +/** + * 地堆货位渲染器 + */ +export default class RackRenderer extends BaseRenderer { + static POINT_NAME = 'ground_store' + + pointMaterial: THREE.Material + + /** + * 默认点的高度, 防止和地面重合 + */ + readonly defulePositionY: number = 0.5 // 默认点的高度, 0.01, 防止和地面重合 + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1.5, 1.2, 0.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, objects: THREE.Object3D[]) { + super.afterCreateOrUpdatePoint(item, option, objects) + + const point = objects[0] + point.position.y = this.defulePositionY + 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), + 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.') + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { + // 创建平面几何体 + + const group = new THREE.Group() + group.name = RackRenderer.POINT_NAME + + // 绘制背景矩形框 + const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth); + planeGeometry.rotateX(-Math.PI / 2) + const planeMaterial = new THREE.MeshBasicMaterial({ + color: 'white', + transparent: true, // 启用透明 + opacity: 0.2, // 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 + ]); + lineGeometry.rotateX(-Math.PI / 2) + const lineMaterial = new LineMaterial({ + color: 0x00ff00, + linewidth: 0.05, + 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) + + return [group] + } + + dispose() { + super.dispose() + this.pointMaterial.dispose() + } +} diff --git a/src/modules/rack/index.ts b/src/modules/rack/index.ts new file mode 100644 index 0000000..4e16547 --- /dev/null +++ b/src/modules/rack/index.ts @@ -0,0 +1,15 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import RackRenderer from './RackRenderer.ts' +import RackEntity from './RackEntity.ts' +import RackMeta from './RackMeta.ts' +import RackInteraction from './RackInteraction.ts' + +export const ITEM_TYPE_NAME = 'rack' + +export default defineModule({ + name: ITEM_TYPE_NAME, + renderer: new RackRenderer(ITEM_TYPE_NAME), + interaction: new RackInteraction(ITEM_TYPE_NAME), + meta: RackMeta, + entity: RackEntity +})