From e921b6600462cb03ed35918f9ba2fe7c89dfa386 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Thu, 5 Jun 2025 17:54:39 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E7=BA=A2=E9=80=89=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/ModelUtils.ts | 23 ++++++++++++++++++++++- src/core/controls/SelectInspect.ts | 7 ++++--- src/core/engine/Viewport.ts | 6 ++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index 3abd3ec..cd4d521 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -140,7 +140,27 @@ export function deletePointByKeyboard() { const entityId = viewport.state.selectedEntityId if (!entityId) { - system.msg('没有选中任何点') + const multiSelectedEntityIds = viewport.state.multiSelectedEntityIds + if (!multiSelectedEntityIds && multiSelectedEntityIds.length === 0) { + system.msg('请选中要删除的实体', 'error') + return + } + + const stateManager = viewport.stateManager + stateManager.beginStateUpdate() + const deleteItems = _.remove(stateManager.vdata.items, (item) => multiSelectedEntityIds.includes(item.id)) + stateManager.endStateUpdate() + if (deleteItems.length === 0) { + system.msg('没有找到要删除的实体', 'error') + } else { + system.msg('删除了 ' + deleteItems.length + ' 个实体') + } + for (const deleteEntityId of multiSelectedEntityIds) { + EventBus.dispatch('entityDeleted', { + deleteEntityId: deleteEntityId + }) + viewport.selectInspect.clearRedSelectionBoxes() + } return } @@ -161,6 +181,7 @@ export function deletePointByKeyboard() { }) system.msg('删除 [' + entityId + ']') + viewport.selectInspect.clearSelectionBox() } export function escByKeyboard() { diff --git a/src/core/controls/SelectInspect.ts b/src/core/controls/SelectInspect.ts index 62c3eff..e008579 100644 --- a/src/core/controls/SelectInspect.ts +++ b/src/core/controls/SelectInspect.ts @@ -167,7 +167,7 @@ export default class SelectInspect implements IControls { * 更新选中对象的包围盒线框 */ updateSelectionBox(selectedObject: THREE.Object3D) { - this.disposeSelectionBox() + this.clearSelectionBox() if (!selectedObject) { return @@ -214,11 +214,12 @@ export default class SelectInspect implements IControls { puFn = undefined // 销毁选择工具 - this.disposeSelectionBox() + this.clearSelectionBox() this.disposeRect() + this.clearRedSelectionBoxes() } - disposeSelectionBox() { + clearSelectionBox() { if (this.selectionBox) { this.viewport.scene.remove(this.selectionBox) this.selectionBox.geometry.dispose() diff --git a/src/core/engine/Viewport.ts b/src/core/engine/Viewport.ts index c87036f..8928f32 100644 --- a/src/core/engine/Viewport.ts +++ b/src/core/engine/Viewport.ts @@ -37,10 +37,12 @@ export default class Viewport { dragControl: any // EsDragControls animationFrameId: any = null scene: SceneHelp + selectInspect = new SelectInspect() + mouseMoveInspect = new MouseMoveInspect() tools: IControls[] = [ - new MouseMoveInspect(), - new SelectInspect() + markRaw(this.selectInspect), + markRaw(this.mouseMoveInspect) ] // 状态管理器 From 93b04ee4be23bb7fa4438321f7eaca4788aae449 Mon Sep 17 00:00:00 2001 From: yuliang <398780299@qq.com> Date: Thu, 5 Jun 2025 18:03:39 +0800 Subject: [PATCH 2/5] =?UTF-8?q?rack=202D=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 | 3 +- src/core/Constract.ts | 1 + src/core/ModelUtils.ts | 19 ++++++++ src/core/manager/WorldModel.ts | 6 ++- src/editor/Model2DEditor.vue | 8 ++++ src/example/example1.js | 28 +++++++++++- src/modules/gstore/GstoreRenderer.ts | 26 +++++------ src/modules/rack/RackRenderer.ts | 84 ++++++++++++++++++++++++++---------- 8 files changed, 136 insertions(+), 39 deletions(-) diff --git a/src/components/Model3DView.vue b/src/components/Model3DView.vue index 110e9db..2f38a72 100644 --- a/src/components/Model3DView.vue +++ b/src/components/Model3DView.vue @@ -386,6 +386,7 @@ function createGroundStore() { side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} }); const planeMesh = new THREE.Mesh(planeGeometry, material); + planeMesh.rotateX(Math.PI / 2) scene.add(planeMesh); } @@ -928,4 +929,4 @@ function cleanupThree() { } } - \ No newline at end of file + diff --git a/src/core/Constract.ts b/src/core/Constract.ts index 34d4c0a..d9ba228 100644 --- a/src/core/Constract.ts +++ b/src/core/Constract.ts @@ -17,6 +17,7 @@ export default Object.freeze({ CursorModeMeasure: 'measure', CursorModeWay: 'way', CursorModeGstore: 'gstore', + CursorModeRack: 'rack', // 选择模式 CursorModeSelectByRec: 'selectByRec' diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index 3abd3ec..7a57028 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -6,6 +6,7 @@ import { computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh' import { Vector2 } from 'three/src/math/Vector2' import type Toolbox from '@/model/itemType/Toolbox.ts' import EventBus from '@/runtime/EventBus.ts' +import Decimal from 'decimal.js' /** * 确保所有实体之间的关系满足一致性: @@ -453,3 +454,21 @@ function loadObject3DFromJson(items: ItemJson[]): THREE.Object3D[] { return result } + +/** + * 十进制求和 + * @param collection + * @param iteratee + */ +export function decimalSumBy(collection: ArrayLike | null | undefined, iteratee?: ((value: T) => number)): number { + + let sum = new Decimal(0) + _.forEach(collection, (t)=>{ + if (typeof iteratee === 'function') { + sum = sum.add(new Decimal(iteratee(t))) + } else { + sum = sum.add(new Decimal(t)) + } + }) + return sum.toNumber() +} diff --git a/src/core/manager/WorldModel.ts b/src/core/manager/WorldModel.ts index 8b6f429..ce62347 100644 --- a/src/core/manager/WorldModel.ts +++ b/src/core/manager/WorldModel.ts @@ -4,6 +4,7 @@ import EventBus from '@/runtime/EventBus' import Measure from '@/modules/measure' import Way from '@/modules/way' import Gstore from '@/modules/gstore' +import Rack from '@/modules/rack' import StateManager from '@/core/manager/StateManager.ts' export interface WorldModelState { @@ -64,7 +65,8 @@ export default class WorldModel { return Promise.all([ Measure, Way, - Gstore + Gstore, + Rack ]).then(() => { console.log('世界模型初始化完成') @@ -155,4 +157,4 @@ export default class WorldModel { } return Promise.reject('楼层不存在, catalogCode=' + catalogCode) } -} \ No newline at end of file +} diff --git a/src/editor/Model2DEditor.vue b/src/editor/Model2DEditor.vue index 136f18f..e30834e 100644 --- a/src/editor/Model2DEditor.vue +++ b/src/editor/Model2DEditor.vue @@ -53,6 +53,14 @@ :type="state?.cursorMode===Constract.CursorModeGstore?'primary':''" @click="()=>state.cursorMode = Constract.CursorModeGstore"> + + + +
b.bayWidth), this.defaultScale.y, item.dt.rackDepth) point.rotation.set( THREE.MathUtils.degToRad(this.defaultRotation.x), THREE.MathUtils.degToRad(this.defaultRotation.y), @@ -49,57 +51,95 @@ export default class RackRenderer extends BaseRenderer { throw new Error('not allow store line.') } - createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { + + createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { // 创建平面几何体 const group = new THREE.Group() group.name = RackRenderer.POINT_NAME + const rackWidth = decimalSumBy(item.dt.bays, b=>b.bayWidth) + const heights = [] + for (let i = 0; i < item.dt.bays.length; i++) { + const bay = item.dt.bays[i] + const bayHeight = decimalSumBy(bay.levelHeight) + heights.push(bayHeight) + } + const rackHeight = _.max(heights) // 绘制背景矩形框 - const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth); - planeGeometry.rotateX(-Math.PI / 2) + const planeGeometry = new THREE.PlaneGeometry(rackWidth, item.dt.rackDepth); + + planeGeometry.rotateX(Math.PI / 2) + const planeMaterial = new THREE.MeshBasicMaterial({ - color: 'white', + color: '#9a9090', 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) { + if (!item.dt.bays || !item.dt.rackDepth) { return [group] } // 绘制边框 - const lineXLen = item.dt.storeWidth - this.defaultLineWidth - const lineYLen = item.dt.storeDepth - this.defaultLineWidth + const lineXLen = rackWidth - this.defaultLineWidth + const lineYLen = item.dt.rackDepth - 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) + -(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: 0x00ff00, + color: '#0d89a5', 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] + let lineDistanceX = 0 + + for (let i = 0; item.dt.bays.length > 1 && i < item.dt.bays.length - 1; i++) { + const bay = item.dt.bays[i] + lineDistanceX += bay.bayWidth + const lineGeometryT = new LineGeometry().setPositions([ + -(lineDistanceX) +(lineXLen/2),0,lineYLen/2, + -(lineDistanceX)+(lineXLen/2),0,-(lineYLen/2) + ]) + const lineT = new Line2(lineGeometryT, lineMaterial); + group.add(lineT as THREE.Object3D) + } + + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + + + item.dt.rackWidth = rackWidth + item.dt.rackHeight = rackHeight + + 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 []; + } } From 5104beeb51b79d6b70c0e6ca21e08d2d7cfb15f0 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Thu, 5 Jun 2025 18:05:27 +0800 Subject: [PATCH 3/5] =?UTF-8?q?HEIGHT=20=E4=BA=A7=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/Constract.ts | 10 ++++++++-- src/modules/gstore/GstoreRenderer.ts | 3 ++- src/modules/measure/MeasureRenderer.ts | 5 +++-- src/modules/rack/RackRenderer.ts | 25 +++++++++++++------------ src/modules/way/WayRenderer.ts | 3 ++- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/core/Constract.ts b/src/core/Constract.ts index 34d4c0a..910ac50 100644 --- a/src/core/Constract.ts +++ b/src/core/Constract.ts @@ -1,4 +1,4 @@ -export default Object.freeze({ +const Constract = Object.freeze({ // 光标相关 CursorModeNormal: 'normal', @@ -19,5 +19,11 @@ export default Object.freeze({ CursorModeGstore: 'gstore', // 选择模式 - CursorModeSelectByRec: 'selectByRec' + CursorModeSelectByRec: 'selectByRec', + + HEIGHT_GSTORE: 0.03, + HEIGHT_MEASURE: 0.02, + HEIGHT_RACK: 0, + HEIGHT_WAY: 0.01 }) +export default Constract diff --git a/src/modules/gstore/GstoreRenderer.ts b/src/modules/gstore/GstoreRenderer.ts index 40c284e..bcb8438 100644 --- a/src/modules/gstore/GstoreRenderer.ts +++ b/src/modules/gstore/GstoreRenderer.ts @@ -3,6 +3,7 @@ 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 Constract from '@/core/Constract.ts' /** * 地堆货位渲染器 @@ -15,7 +16,7 @@ export default class GstoreRenderer extends BaseRenderer { /** * 默认点的高度, 防止和地面重合 */ - readonly defulePositionY: number = 0.5 // 默认点的高度, 0.01, 防止和地面重合 + readonly defulePositionY: number = Constract.HEIGHT_GSTORE 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 diff --git a/src/modules/measure/MeasureRenderer.ts b/src/modules/measure/MeasureRenderer.ts index 610e214..18b6d60 100644 --- a/src/modules/measure/MeasureRenderer.ts +++ b/src/modules/measure/MeasureRenderer.ts @@ -8,6 +8,7 @@ import { numberToString } from '@/utils/webutils.ts' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { Text } from 'troika-three-text' import SimSunTTF from '@/assets/fonts/simsunb.ttf' +import Constract from '@/core/Constract.ts' /** * 辅助测量工具渲染器 @@ -28,7 +29,7 @@ export default class MeasureRenderer extends BaseRenderer { pointMaterial: THREE.Material lineMaterial: LineMaterial - readonly defulePositionY = 0.01 + readonly defulePositionY = Constract.HEIGHT_MEASURE readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.1, 0.1, 0.1) readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0) @@ -211,4 +212,4 @@ export default class MeasureRenderer extends BaseRenderer { this.pointMaterial.dispose() this.lineMaterial.dispose() } -} \ No newline at end of file +} diff --git a/src/modules/rack/RackRenderer.ts b/src/modules/rack/RackRenderer.ts index e6d5984..b058e64 100644 --- a/src/modules/rack/RackRenderer.ts +++ b/src/modules/rack/RackRenderer.ts @@ -3,6 +3,7 @@ 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 Constract from '@/core/Constract.ts' /** * 地堆货位渲染器 @@ -15,7 +16,7 @@ export default class RackRenderer extends BaseRenderer { /** * 默认点的高度, 防止和地面重合 */ - readonly defulePositionY: number = 0.5 // 默认点的高度, 0.01, 防止和地面重合 + readonly defulePositionY: number = Constract.HEIGHT_WAY 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 @@ -56,7 +57,7 @@ export default class RackRenderer extends BaseRenderer { group.name = RackRenderer.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 +65,8 @@ export default class RackRenderer 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 +78,12 @@ export default class RackRenderer 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 +91,9 @@ export default class RackRenderer 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] diff --git a/src/modules/way/WayRenderer.ts b/src/modules/way/WayRenderer.ts index ac468ab..4f0f3c2 100644 --- a/src/modules/way/WayRenderer.ts +++ b/src/modules/way/WayRenderer.ts @@ -6,6 +6,7 @@ import SimSunTTF from '@/assets/fonts/simsunb.ttf' import { getLineId } from '@/core/ModelUtils.ts' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { numberToString } from '@/utils/webutils.ts' +import Constract from '@/core/Constract.ts' /** * 辅助测量工具渲染器 @@ -28,7 +29,7 @@ export default class WayRenderer extends BaseRenderer { /** * 默认点的高度, 防止和地面重合 */ - readonly defulePositionY: number = 0.8 // 默认点的高度, 0.01, 防止和地面重合 + readonly defulePositionY: number = Constract.HEIGHT_WAY readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.25, 0.1) readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0) From 57e8749002f94343807648b5a52b810f4433a0b5 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Thu, 5 Jun 2025 18:14:01 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=B1=9E=E6=80=A7=E9=9D=A2=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/editor/widgets/property/PropertyView.vue | 60 +++++++++++++++++----------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/src/editor/widgets/property/PropertyView.vue b/src/editor/widgets/property/PropertyView.vue index 67acf02..d743f1f 100644 --- a/src/editor/widgets/property/PropertyView.vue +++ b/src/editor/widgets/property/PropertyView.vue @@ -1,23 +1,26 @@