From 47e2c53e19e8263c4cee2a8055866a072551b848 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Thu, 5 Jun 2025 16:50:28 +0800 Subject: [PATCH] =?UTF-8?q?3D=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/Constract.ts | 5 +- src/core/ModelUtils.ts | 20 ++++- src/core/base/BaseRenderer.ts | 13 ++-- src/core/controls/DragControls.js | 140 +++++++++++++++++++++++------------ src/core/controls/EsDragControls.ts | 23 +----- src/core/controls/SelectInspect.ts | 2 +- src/core/engine/Viewport.ts | 100 ++++++++++++++++++++----- src/core/manager/EntityManager.ts | 9 ++- src/editor/Model2DEditor.vue | 11 ++- src/modules/gstore/GstoreRenderer.ts | 24 +++--- src/modules/rack/RackMeta.ts | 43 ++++++++++- 11 files changed, 276 insertions(+), 114 deletions(-) diff --git a/src/core/Constract.ts b/src/core/Constract.ts index b97c525..34d4c0a 100644 --- a/src/core/Constract.ts +++ b/src/core/Constract.ts @@ -10,6 +10,9 @@ export default Object.freeze({ CursorModeLinkAdd: 'LinkAdd', CursorModeLinkAdd2: 'LinkAdd2', + Mode2D: '2D', + Mode3D: '3D', + // 测量相关的光标模式 CursorModeMeasure: 'measure', CursorModeWay: 'way', @@ -17,4 +20,4 @@ export default Object.freeze({ // 选择模式 CursorModeSelectByRec: 'selectByRec' -}) \ No newline at end of file +}) diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index 2fe0620..3abd3ec 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -100,6 +100,24 @@ export function getLineId(startId: string, endId: string, type: LinkType): strin } /** + * 获取某个 Object3D 先上查找最近的有效业务 Object3D + * @param object + */ +export function getClosestObject(object: THREE.Object3D) { + if (!object) { + return undefined + } + while (object) { + if (object.userData && object.userData.t && object.userData.entityId) { + // 找到第一个有效的业务 Object3D + return object + } + // 向上查找父级 + object = object.parent + } +} + +/** * 解析线条 ID, 返回 线条类型, 起点 ID 和终点 ID */ export function parseLineId(lineId): [LinkType, string, string] { @@ -434,4 +452,4 @@ function loadObject3DFromJson(items: ItemJson[]): THREE.Object3D[] { } return result -} \ No newline at end of file +} diff --git a/src/core/base/BaseRenderer.ts b/src/core/base/BaseRenderer.ts index e65a077..1455622 100644 --- a/src/core/base/BaseRenderer.ts +++ b/src/core/base/BaseRenderer.ts @@ -79,7 +79,7 @@ export default abstract class BaseRenderer { } fillObjectUserDataFromItem(item: ItemJson, ...objects: THREE.Object3D[]) { - _.forEach(objects, (object) => { + for (const object of objects) { if (!object.name && item.name) { object.name = item.name } @@ -90,12 +90,12 @@ export default abstract class BaseRenderer { draggable: item.dt.protected !== true, t: item.t } - }) + } } fillObjectUserDataFromLine(start: ItemJson, end: ItemJson, type: LinkType, ...objects: THREE.Object3D[]) { const id = getLineId(start.id, end.id, type) - _.forEach(objects, (object) => { + for (const object of objects) { if (!object.name) { object.name = id } @@ -105,7 +105,7 @@ export default abstract class BaseRenderer { entityId: getLineId(start.id, end.id, type), t: start.t } - }) + } } /** @@ -116,9 +116,10 @@ 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') + this.tempViewport.scene.add(...objects) + const dragObjects = objects.filter(obj => !!obj.userData.draggable) + this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') } removeFromScene(...objects: THREE.Object3D[]) { diff --git a/src/core/controls/DragControls.js b/src/core/controls/DragControls.js index a4a7c74..7e777df 100644 --- a/src/core/controls/DragControls.js +++ b/src/core/controls/DragControls.js @@ -1,38 +1,45 @@ -import { - EventDispatcher, - Matrix4, - Plane, - Raycaster, - Vector2, - Vector3 -} from 'three' -import { calcPositionUseSnap } from '@/core/ModelUtils.js' - -const _plane = new Plane() -const _raycaster = new Raycaster() - -const _pointer = new Vector2() -const _offset = new Vector3() -const _intersection = new Vector3() -const _worldPosition = new Vector3() -const _inverseMatrix = new Matrix4() +import { EventDispatcher, Matrix4, Plane, Raycaster, Vector2, Vector3 } from 'three' +import { calcPositionUseSnap, getClosestObject } from '@/core/ModelUtils.js' + +const _plane = new Plane() // 用于拖拽操作的平面 +const _raycaster = new Raycaster() // 射线检测器,用于拾取物体 + +const _pointer = new Vector2() // 屏幕坐标指针位置 (归一化设备坐标) +const _offset = new Vector3() // 拖动时相对于点击点的偏移量 +const _intersection = new Vector3() // 与平面相交的点 +const _worldPosition = new Vector3() // 世界坐标位置 +const _inverseMatrix = new Matrix4() // 用于将位置转换到局部空间 + +/** + * DragControls 控制器类 + * 提供基于鼠标或触摸的拖拽交互功能,并支持 hover 和 clickblank 等事件 + */ class DragControls extends EventDispatcher { + /** + * 构造函数 + * @param _objects 可拖拽的对象数组 + * @param _camera 当前使用的相机 + * @param _domElement 绑定事件的目标 DOM 元素(通常是 canvas) + */ constructor(_objects, _camera, _domElement) { super() - _domElement.style.touchAction = 'none' // disable touch scroll - - let _selected = null, _hovered = null + // 禁止触摸滚动行为 + _domElement.style.touchAction = 'none' - const _intersections = [] + let _selected = null // 当前选中(正在拖动)的对象 + let _hovered = null // 当前悬停的对象 - // + const _intersections = [] // 存储射线检测结果的数组 - let isMove = false - let isMouseDownClicked = false - const scope = this + let isMove = false // 标记是否发生了移动 + let isMouseDownClicked = false // 标记是否按下了鼠标 + const scope = this // 保存当前上下文 + /** + * 激活事件监听器 + */ function activate() { _domElement.addEventListener('pointermove', onPointerMove) _domElement.addEventListener('pointerdown', onPointerDown) @@ -40,6 +47,9 @@ class DragControls extends EventDispatcher { _domElement.addEventListener('pointerleave', onPointerCancel) } + /** + * 去激活事件监听器 + */ function deactivate() { _domElement.removeEventListener('pointermove', onPointerMove) _domElement.removeEventListener('pointerdown', onPointerDown) @@ -49,22 +59,41 @@ class DragControls extends EventDispatcher { _domElement.style.cursor = '' } + /** + * 销毁控制器并释放资源 + */ function dispose() { deactivate() } + /** + * 设置可拖拽的对象列表 + * @param {Array} objects - 新的对象数组 + */ function setObjects(objects) { _objects = objects } + /** + * 获取当前可拖拽的对象列表 + * @returns {Array} + */ function getObjects() { return _objects } + /** + * 获取内部的 Raycaster 实例 + * @returns {Raycaster} + */ function getRaycaster() { return _raycaster } + /** + * 鼠标/指针移动事件处理函数 + * 处理悬停、拖拽等交互逻辑 + */ function onPointerMove(event) { if (!scope.enabled || !scope.enabledMove) return @@ -76,6 +105,7 @@ class DragControls extends EventDispatcher { updatePointer(event) _raycaster.setFromCamera(_pointer, _camera) + // 如果有选中的对象,则更新其位置 if (_selected) { if (_raycaster.ray.intersectPlane(_plane, _intersection)) { const pos = _intersection.sub(_offset).applyMatrix4(_inverseMatrix) @@ -86,7 +116,7 @@ class DragControls extends EventDispatcher { return } - // hover support + // 鼠标/笔悬停检测 if (event.pointerType === 'mouse' || event.pointerType === 'pen') { _intersections.length = 0 @@ -98,24 +128,21 @@ class DragControls extends EventDispatcher { const object = _intersections[0].object - _plane.setFromNormalAndCoplanarPoint(_camera.getWorldDirection(_plane.normal), _worldPosition.setFromMatrixPosition(object.matrixWorld)) + _plane.setFromNormalAndCoplanarPoint( + _camera.getWorldDirection(_plane.normal), + _worldPosition.setFromMatrixPosition(object.matrixWorld) + ) if (_hovered !== object && _hovered !== null) { - scope.dispatchEvent({ type: 'hoveroff', object: _hovered }) - _domElement.style.cursor = 'auto' _hovered = null - } if (_hovered !== object) { - scope.dispatchEvent({ type: 'hoveron', object: object }) - _domElement.style.cursor = 'pointer' _hovered = object - } } else { @@ -132,6 +159,10 @@ class DragControls extends EventDispatcher { } + /** + * 鼠标按下事件处理函数 + * 检测是否点击了可拖拽对象,并准备开始拖拽 + */ function onPointerDown(event) { if (scope.enabled === false) return @@ -145,41 +176,58 @@ class DragControls extends EventDispatcher { _raycaster.intersectObjects(objects, true, _intersections) if (_intersections.length > 0) { + // 判断是否启用组拖动模式 _selected = (scope.transformGroup === true) ? _objects[0] : _intersections[0].object if (scope.enabledMove) { - _plane.setFromNormalAndCoplanarPoint(_camera.getWorldDirection(_plane.normal), _worldPosition.setFromMatrixPosition(_selected.matrixWorld)) + // 设置拖拽平面 + _plane.setFromNormalAndCoplanarPoint( + _camera.getWorldDirection(_plane.normal), + _worldPosition.setFromMatrixPosition(_selected.matrixWorld) + ) if (_raycaster.ray.intersectPlane(_plane, _intersection)) { + // 计算偏移量 _inverseMatrix.copy(_selected.parent.matrixWorld).invert() - _offset.copy(_intersection).sub(_worldPosition.setFromMatrixPosition(_selected.matrixWorld)) + _offset.copy(_intersection).sub( + _worldPosition.setFromMatrixPosition(_selected.matrixWorld) + ) } - // setTimeout(() => { - // _domElement.style.cursor = 'move' - // }, 20) isMouseDownClicked = true } + + // 触发 dragstart 事件 scope.dispatchEvent({ type: 'dragstart', object: _selected, e: event }) } isMove = false } + /** + * 鼠标释放或离开事件处理函数 + * 结束拖拽操作或触发点击空白区域事件 + */ function onPointerCancel(event) { if (scope.enabled === false) return if (_selected) { + // 结束拖拽 scope.dispatchEvent({ type: 'dragend', object: _selected, e: event }) _selected = null } else if (!isMove) { - // 添加点击空白处的事件 + // 如果没有发生移动,则认为是点击空白处 scope.dispatchEvent({ type: 'clickblank', e: event }) } + // 恢复光标状态 _domElement.style.cursor = _hovered ? 'pointer' : 'auto' isMouseDownClicked = false } + /** + * 更新指针位置 + * 将屏幕坐标转换为 NDC 设备坐标 (-1 ~ 1) + */ function updatePointer(event) { const rect = _domElement.getBoundingClientRect() @@ -187,13 +235,13 @@ class DragControls extends EventDispatcher { _pointer.y = -(event.clientY - rect.top) / rect.height * 2 + 1 } + // 初始化:激活事件监听器 activate() - // API - - this.enabled = true - this.enabledMove = true - this.transformGroup = false + // 暴露 API 方法和属性 + this.enabled = true // 是否启用控制器 + this.enabledMove = true // 是否允许移动操作 + this.transformGroup = false // 是否以组形式变换多个对象 this.activate = activate this.deactivate = deactivate @@ -201,9 +249,7 @@ class DragControls extends EventDispatcher { this.setObjects = setObjects this.getObjects = getObjects this.getRaycaster = getRaycaster - } - } export { DragControls } diff --git a/src/core/controls/EsDragControls.ts b/src/core/controls/EsDragControls.ts index 6a0b66e..83f4935 100644 --- a/src/core/controls/EsDragControls.ts +++ b/src/core/controls/EsDragControls.ts @@ -71,7 +71,7 @@ export default class EsDragControls { // 拖拽开始 dragControlsStart(e) { // 右键拖拽不响应 - if (e.e.button === 2 || !e.object.visible) return + if (e.e.button === 2 || !e.object?.visible) return const type = e.object.userData?.t const entityId = e.object.userData?.entityId @@ -127,25 +127,6 @@ export default class EsDragControls { if (e.object.userData.onClick) { e.object.userData.onClick(e) } - if (e.object.userData?.entityId) { - const entityId = e.object.userData.entityId - const item = this.viewport.entityManager.findItemById(entityId) - const itemTypeName = e.object.userData.t - if (item.dt.protected !== true) { - this.viewport.state.selectedObject = markRaw(e.object) - this.viewport.state.selectedItem = markRaw(item) - this.viewport.state.selectedEntityId = entityId - this.viewport.state.selectedObjectMeta = getMeta(itemTypeName) - - EventBus.dispatch('selectedObjectChanged', { - viewport: this, - selectedObject: this.viewport.state.selectedObject, - selectedItem: this.viewport.state.selectedItem, - selectedEntityId: this.viewport.state.selectedEntityId, - selectedObjectMeta: this.viewport.state.selectedObjectMeta - }) - } - } } const ret = this.currentInteraction?.dragPointComplete(this.viewport, e) @@ -182,4 +163,4 @@ export default class EsDragControls { this.dragControls.removeEventListener('dragend', dragEndFn) this.dragControls.dispose() } -} \ No newline at end of file +} diff --git a/src/core/controls/SelectInspect.ts b/src/core/controls/SelectInspect.ts index f2def9f..7f54c3c 100644 --- a/src/core/controls/SelectInspect.ts +++ b/src/core/controls/SelectInspect.ts @@ -235,6 +235,7 @@ export default class SelectInspect implements IControls { // 如果是点击事件,触发选中逻辑 const objects: THREE.Object3D[] = this.viewport.entityManager.getObjectByCanvasMouse(event) if (objects.length > 0) { + console.log('mouseClick', objects) const object = objects[0] const entityId = object.userData.entityId const item = this.viewport.entityManager.findItemById(entityId) @@ -244,7 +245,6 @@ export default class SelectInspect implements IControls { 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, diff --git a/src/core/engine/Viewport.ts b/src/core/engine/Viewport.ts index f1f41b2..557ad11 100644 --- a/src/core/engine/Viewport.ts +++ b/src/core/engine/Viewport.ts @@ -20,6 +20,7 @@ import InteractionManager from '@/core/manager/InteractionManager' import { calcPositionUseSnap } from '@/core/ModelUtils' import StateManager from '@/core/manager/StateManager.ts' import EventBus from '@/runtime/EventBus.ts' +import Constract from '@/core/Constract.ts' import type { IMeta } from '@/core/base/IMeta.ts' /** @@ -28,7 +29,7 @@ import type { IMeta } from '@/core/base/IMeta.ts' */ export default class Viewport { viewerDom: HTMLElement - camera: THREE.OrthographicCamera + camera: THREE.Camera // THREE.OrthographicCamera renderer: THREE.WebGLRenderer statsControls: Stats controls: OrbitControls @@ -66,6 +67,7 @@ export default class Viewport { isUpdating: false, cursorMode: 'normal', selectedObject: null, + view3DMode: Constract.Mode2D, camera: { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 } @@ -142,7 +144,14 @@ export default class Viewport { this.renderer = renderer // 创建正交摄像机 - this.initMode2DCamera() + // this.initMode2DCamera() + this.watchList.push(watch(() => this.state.view3DMode, (newVal) => { + if (newVal === Constract.Mode3D) { + this.initMode3DCamera() + } else { + this.initMode2DCamera() + } + }, { immediate: true })) // 注册拖拽组件 this.dragControl = new EsDragControls(this) @@ -165,6 +174,7 @@ export default class Viewport { this.updateGridVisibility() })) + // 监听窗口大小变化 if (this.resizeObserver) { this.resizeObserver.unobserve(this.viewerDom) @@ -211,6 +221,44 @@ export default class Viewport { } /** + * 初始化3D相机 + */ + initMode3DCamera() { + if (this.camera) { + this.scene.remove(this.camera) + } + + // ============================ 创建透视相机 + const viewerDom = this.viewerDom + const cameraNew = new THREE.PerspectiveCamera( + 25, + viewerDom.clientWidth / viewerDom.clientHeight, + 1, + 2000 + ) + cameraNew.position.set(4, 2, -3) + cameraNew.lookAt(0, 0, 0) + this.camera = cameraNew + this.scene.add(this.camera) + + // ============================ 创建控制器 + const controls = new OrbitControls( + this.camera, + this.renderer?.domElement + ) + controls.enableDamping = false + controls.screenSpacePanning = false // 定义平移时如何平移相机的位置 控制不上下移动 + controls.minDistance = 2 + controls.maxDistance = 1000 + controls.mouseButtons = { LEFT: THREE.MOUSE.PAN, RIGHT: THREE.MOUSE.ROTATE } + // 下面这句话非常影响性能 + // this.controls.addEventListener('change', ()=>{ + // this.renderer.render(this.scene, this.camera); + // }); + this.controls = controls + } + + /** * 初始化2D相机 */ initMode2DCamera() { @@ -253,7 +301,7 @@ export default class Viewport { controlsNew.maxDistance = 1000 this.controls = controlsNew - this.camera.updateProjectionMatrix() + cameraNew.updateProjectionMatrix() this.syncCameraState() } @@ -290,26 +338,32 @@ export default class Viewport { syncCameraState() { if (this.camera) { const camera = this.camera + if (this.camera instanceof THREE.PerspectiveCamera) { + this.state.camera.position.x = camera.position.x + this.state.camera.position.y = (camera as THREE.PerspectiveCamera).zoom // this.getEffectiveViewDistance() + this.state.camera.position.z = camera.position.z - this.state.camera.position.x = camera.position.x - this.state.camera.position.y = camera.zoom // this.getEffectiveViewDistance() - this.state.camera.position.z = camera.position.z + } else { + this.state.camera.position.x = camera.position.x + this.state.camera.position.y = 5 + this.state.camera.position.z = camera.position.z + } } } - /** - * 计算相机到目标的有效视距 - */ - getEffectiveViewDistance() { - if (!this.camera) { - return 10 - } - const camera = this.camera - const viewHeight = (camera.top - camera.bottom) / camera.zoom - // 假设我们希望匹配一个虚拟的透视相机(通常使用45度fov作为参考) - const referenceFOV = 45 // 参考视场角 - return viewHeight / (2 * Math.tan(THREE.MathUtils.degToRad(referenceFOV) / 2)) - } + // /** + // * 计算相机到目标的有效视距 + // */ + // getEffectiveViewDistance() { + // if (!this.camera || !(this.camera instanceof THREE.PerspectiveCamera)) { + // return 5 + // } + // const camera = this.camera as THREE.PerspectiveCamera + // const viewHeight = (camera.top - camera.bottom) / camera.zoom + // // 假设我们希望匹配一个虚拟的透视相机(通常使用45度fov作为参考) + // const referenceFOV = 45 // 参考视场角 + // return viewHeight / (2 * Math.tan(THREE.MathUtils.degToRad(referenceFOV) / 2)) + // } handleResize(entries: any) { for (let entry of entries) { @@ -358,6 +412,12 @@ export default class Viewport { * 根据可视化范围更新网格的透明度 */ updateGridVisibility() { + if (this.camera === undefined || !(this.camera instanceof THREE.PerspectiveCamera)) { + // 如果没有相机或相机不是透视相机,则不更新网格可见性 + this.gridHelper.visible = true + this.gridHelper.material.opacity = 1 + return + } const cameraDistance = this.state.camera.position.y const maxVisibleDistance = 4 // 网格完全不可见的最小距离 const fadeStartDistance = 9 // 开始淡出的最大距离 @@ -498,6 +558,8 @@ export interface ViewportState { selectedEntityId: string | undefined + view3DMode: string // Constract.Mode2D | Constract.Mode3D + /** * 选中的对象的元数据 */ diff --git a/src/core/manager/EntityManager.ts b/src/core/manager/EntityManager.ts index a09fc19..fb88c4f 100644 --- a/src/core/manager/EntityManager.ts +++ b/src/core/manager/EntityManager.ts @@ -2,7 +2,7 @@ import * as THREE from 'three' 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 { getClosestObject, getLineId, parseLineId } from '@/core/ModelUtils' import { Vector2 } from 'three' /** @@ -541,11 +541,12 @@ export default class EntityManager { if (!_intersections || _intersections.length === 0) { return [] } + // 根据距离排序射线命中的对象集 return _.map( _intersections.sort((a, b) => a.distance - b.distance), - r => r.object - ) + r => getClosestObject(r.object) + ).filter(obj => obj?.userData && obj.userData.selectable !== false) } } @@ -585,4 +586,4 @@ export class Relation { else throw new Error(`Unknown link type: ${type}`) } -} \ No newline at end of file +} diff --git a/src/editor/Model2DEditor.vue b/src/editor/Model2DEditor.vue index 364a4fd..136f18f 100644 --- a/src/editor/Model2DEditor.vue +++ b/src/editor/Model2DEditor.vue @@ -7,6 +7,15 @@ + + + 2D + 3D +
\ No newline at end of file + diff --git a/src/modules/gstore/GstoreRenderer.ts b/src/modules/gstore/GstoreRenderer.ts index 96fcc95..40c284e 100644 --- a/src/modules/gstore/GstoreRenderer.ts +++ b/src/modules/gstore/GstoreRenderer.ts @@ -56,7 +56,7 @@ export default class GstoreRenderer extends BaseRenderer { group.name = GstoreRenderer.POINT_NAME // 绘制背景矩形框 - const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth); + const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth) planeGeometry.rotateX(-Math.PI / 2) const planeMaterial = new THREE.MeshBasicMaterial({ color: 'white', @@ -64,8 +64,8 @@ export default class GstoreRenderer extends BaseRenderer { opacity: 0.2, // 50%透明度 depthWrite: false, // 防止深度冲突 side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} - }); - const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); + }) + const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial) group.add(planeMesh) if (!item.dt.storeWidth || !item.dt.storeDepth) { @@ -77,12 +77,12 @@ export default class GstoreRenderer extends BaseRenderer { 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), -(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, @@ -90,9 +90,9 @@ export default class GstoreRenderer extends BaseRenderer { worldUnits: true, resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), side: THREE.DoubleSide - }); + }) // - const line = new Line2(lineGeometry, lineMaterial); + const line = new Line2(lineGeometry, lineMaterial) group.add(line as THREE.Object3D) return [group] @@ -100,6 +100,6 @@ export default class GstoreRenderer extends BaseRenderer { dispose() { super.dispose() - this.pointMaterial.dispose() + this.pointMaterial?.dispose() } } diff --git a/src/modules/rack/RackMeta.ts b/src/modules/rack/RackMeta.ts index 2273048..77b81e9 100644 --- a/src/modules/rack/RackMeta.ts +++ b/src/modules/rack/RackMeta.ts @@ -7,8 +7,49 @@ export default [ { editor: 'TransformEditor', category: 'basic' }, { field: 'dt.color', editor: 'Color', label: '颜色', category: 'basic' }, { editor: '-', category: 'basic' }, + + + { field: 'dt.rackDepth', editor: 'NumberInput', label: '货架深度', category: 'basic' }, + { field: 'dt.levelCount', editor: 'NumberInput', label: '总层数', category: 'basic' }, + { field: 'dt.bayCount', editor: 'NumberInput', label: '总列数', category: 'basic' }, + { field: 'dt.hideFloor', editor: 'NumberInput', label: '隐藏底板', category: 'basic' }, + { field: 'dt.extendColumns', editor: 'NumberInput', label: '扩展挡板', category: 'basic' }, + { field: 'dt.columnSpacing', editor: 'NumberInput', label: '支脚跨越', category: 'basic' }, + { field: 'dt.bays', editor: 'BayEditor', 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 \ No newline at end of file +] as IMeta