From 0da7a5e88b03b6e93546c63a12985b7f37255e17 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Tue, 10 Jun 2025 22:05:41 +0800 Subject: [PATCH] =?UTF-8?q?gstore=20=E6=8B=96=E6=8B=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ThreePerfView2.vue | 828 +++++++++++++++++++++++++++++ src/core/Constract.ts | 3 +- src/core/IfxUtils.ts | 130 +++++ src/core/base/BaseRenderer.ts | 70 +-- src/core/engine/Viewport.ts | 33 ++ src/core/manager/EntityManager.ts | 36 +- src/editor/menus/Model3DView.ts | 16 + src/example/example1.js | 20 + src/modules/gstore/GstorePropertySetter.ts | 26 +- src/modules/gstore/GstoreRenderer.ts | 166 +++--- src/modules/measure/MeasureRenderer.ts | 122 +---- src/types/ModelTypes.ts | 18 + 12 files changed, 1229 insertions(+), 239 deletions(-) create mode 100644 src/components/ThreePerfView2.vue create mode 100644 src/core/IfxUtils.ts diff --git a/src/components/ThreePerfView2.vue b/src/components/ThreePerfView2.vue new file mode 100644 index 0000000..ddb8147 --- /dev/null +++ b/src/components/ThreePerfView2.vue @@ -0,0 +1,828 @@ + + + diff --git a/src/core/Constract.ts b/src/core/Constract.ts index 38df017..2755a63 100644 --- a/src/core/Constract.ts +++ b/src/core/Constract.ts @@ -33,6 +33,7 @@ const Constract = Object.freeze({ HEIGHT_WAY_LABEL: 0.03, HEIGHT_WAY_LINE: 0.02, - MAX_MEASURE_INSTANCES: 20000, + MAX_MEASURE_INSTANCES: 1000, + MAX_GSTORE_INSTANCES: 500, }) export default Constract diff --git a/src/core/IfxUtils.ts b/src/core/IfxUtils.ts new file mode 100644 index 0000000..adcf52b --- /dev/null +++ b/src/core/IfxUtils.ts @@ -0,0 +1,130 @@ +import * as THREE from 'three' +import { BasePlane } from '@/types/ModelTypes.ts' + +/** + * 创建一个矩形的 Shape + */ +export function buildRectangle(width: number, height: number, padding: number[] | number = 0, offsetX = 0, offsetY = 0): THREE.Shape { + let pl = 0, + pt = 0, + pr = 0, + pb = 0 + + if (typeof padding == 'number') { + pl = padding + pt = padding + pr = padding + pb = padding + } else if (Array.isArray(padding)) { + if (padding.length == 1) { + pl = padding[0] + pt = padding[0] + pr = padding[0] + pb = padding[0] + } else if (padding.length > 1 && padding.length < 4) { + pl = padding[0] + pt = padding[1] + pr = padding[0] + pb = padding[1] + } else if (padding.length >= 4) { + pl = padding[0] + pt = padding[1] + pr = padding[2] + pb = padding[3] + } + } + const shape = new THREE.Shape() + shape.moveTo(0 + pl + offsetX, 0 + pb + offsetY) + shape.lineTo(width - pr + offsetX, 0 + pb + offsetY) + shape.lineTo(width - pr + offsetX, height - pt + offsetY) + shape.lineTo(0 + pl + offsetX, height - pt + offsetY) + shape.closePath() + return shape +} + + +/** + * 根据自定义的 Shape,通过放样得到一个实体。默认实体的样式是在 front 面放样的。 + * + * @param {*} shape 自定义的型状 + * @param {*} depth 放样深度(放样后的物体厚度) + * @param {*} basePlane 放样的基准面。 + * @param {*} x 目标定位x + * @param {*} y 目标定位y + * @param {*} z 目标定位z + * @param {*} euler 旋转到目标位 + * @param {*} anchor 锚点 + * @returns THREE.ExtrudeGeometry + */ +export function createExtrudeItem(shape: THREE.Shape, depth: number, basePlane: number, + x = 0, y = 0, z = 0, euler = null, + anchor = new THREE.Vector3(0, 0, 0)) { + const geometry = new THREE.ExtrudeGeometry(shape, { + steps: 1, + depth: -depth, + bevelEnabled: false, + bevelThickness: 0, + bevelSize: 0, + bevelOffset: 0, + bevelSegments: 0 + }) + + geometry.center() + const size = geometry.boundingBox.getSize(new THREE.Vector3()) + + let dx, dy, dz + + switch (basePlane) { + case BasePlane.LEFT: + geometry.rotateY(Math.PI / 2) + dx = size.z / 2 + dy = size.y / 2 + dz = size.x / 2 + break + case BasePlane.RIGHT: + geometry.rotateY(-Math.PI / 2) + dx = size.z / 2 + dy = size.y / 2 + dz = size.x / 2 + break + case BasePlane.BEHIND: + geometry.translate(0, 0, size.z) + dx = size.x / 2 + dy = size.y / 2 + dz = size.z / 2 + break + case BasePlane.TOP: + geometry.rotateZ(Math.PI) + geometry.rotateX(Math.PI / 2) + geometry.translate(size.x, -size.z, 0) + dx = size.x / 2 + dy = size.z / 2 + dz = size.y / 2 + break + case BasePlane.BOTTOM: + geometry.rotateX(-Math.PI / 2) + dx = size.x / 2 + dy = size.z / 2 + dz = size.y / 2 + break + default: + //BasePlane.FRONT: + dx = size.x / 2 + dy = size.y / 2 + dz = size.z / 2 + break + } + + if (euler != null && euler.isEuler) { + // 注意,需要先旋转,再平移。 + geometry.rotateX(euler.x) + geometry.rotateY(euler.y) + geometry.rotateZ(euler.z) + } + + geometry.translate(dx + x + anchor.x, dy + y + anchor.y, -dz + z + anchor.z) + geometry.center() + return geometry +} + + diff --git a/src/core/base/BaseRenderer.ts b/src/core/base/BaseRenderer.ts index 3cd9622..f0e6786 100644 --- a/src/core/base/BaseRenderer.ts +++ b/src/core/base/BaseRenderer.ts @@ -31,48 +31,6 @@ export default abstract class BaseRenderer { return Promise.resolve() } - get pointManager(): InstancePointManager { - if (!this.tempViewport) { - throw new Error('tempViewport is not set. Please call beginRendererUpdate first.') - } - let pointManager = this.tempViewport.pointManagerMap.get(this.itemTypeName) - if (!pointManager) { - pointManager = this.createPointManager() - if (pointManager) { - this.tempViewport.pointManagerMap.set(this.itemTypeName, pointManager) - } - } - return pointManager - } - - get lineSegmentManager(): LineSegmentManager { - if (!this.tempViewport) { - throw new Error('tempViewport is not set. Please call beginRendererUpdate first.') - } - let lineSegmentManager = this.tempViewport.lineSegmentManagerMap.get(this.itemTypeName) - if (!lineSegmentManager) { - lineSegmentManager = this.createLineSegmentManager() - if (lineSegmentManager) { - this.tempViewport.lineSegmentManagerMap.set(this.itemTypeName, lineSegmentManager) - } - } - return lineSegmentManager - } - - /** - * 创建点管理器 - */ - createPointManager(): InstancePointManager { - return null - } - - /** - * 创建线段管理器 - */ - createLineSegmentManager(): LineSegmentManager { - return null - } - /** * 开始更新 * @param viewport 当前视口 @@ -153,7 +111,6 @@ export default abstract class BaseRenderer { createPointForEntity(item: ItemJson, option?: RendererCudOption): Object3DLike { const point = this.createPoint(item, option) - point.visible = ((typeof item.v !== 'undefined') ? item.v : true) setUserDataForItem(item, point) @@ -202,12 +159,21 @@ export default abstract class BaseRenderer { } updatePointForEntity(item: ItemJson, option?: RendererCudOption): Object3DLike { - const point = this.updatePoint(item, option) - point.visible = ((typeof item.v !== 'undefined') ? item.v : true) + const originObject = this.tempViewport.entityManager.findObjectById(item.id) + const newObject = this.updatePoint(item, originObject, option) + newObject.visible = ((typeof item.v !== 'undefined') ? item.v : true) - setUserDataForItem(item, point) - this.afterCreateOrUpdatePoint(item, option, point) - return point + if (originObject !== newObject) { + // 如果更新后的对象和原来的对象不同, 则替换掉 + setUserDataForItem(item, newObject) + + this.removeFromScene(originObject as THREE.Object3D) + this.appendToScene(newObject as THREE.Object3D) + this.tempViewport.entityManager.replaceObject(item.id, newObject) + } + + this.afterCreateOrUpdatePoint(item, option, newObject) + return newObject } /** @@ -215,13 +181,7 @@ export default abstract class BaseRenderer { * @param item 点的定义 * @param option 渲染选项 */ - updatePoint(item: ItemJson, option?: RendererCudOption): Object3DLike { - const object = this.tempViewport.entityManager.findObjectById(item.id) - if (!object) { - console.warn(`Point with ID "${item.id}" does not exist.`) - return - } - + updatePoint(item: ItemJson, object: Object3DLike, option?: RendererCudOption): Object3DLike { const point = object point.name = item.name || point.name diff --git a/src/core/engine/Viewport.ts b/src/core/engine/Viewport.ts index 3bf8795..207ec27 100644 --- a/src/core/engine/Viewport.ts +++ b/src/core/engine/Viewport.ts @@ -109,6 +109,38 @@ export default class Viewport { } /** + * 获取或创建点管理器 + * @param typeName 点类型名称 + * @param createFn 创建点管理器的函数 + */ + getOrCreatePointManager(typeName: string, createFn: () => InstancePointManager): InstancePointManager { + let pointManager = this.pointManagerMap.get(typeName) + if (!pointManager) { + pointManager = createFn() + if (pointManager) { + this.pointManagerMap.set(typeName, pointManager) + } + } + return pointManager + } + + /** + * 获取或创建线段管理器 + * @param typeName 线段类型名称 + * @param createFn 创建线段管理器的函数 + */ + getOrCreateLineManager(typeName: string, createFn: () => LineSegmentManager): LineSegmentManager { + let lineSegmentManager = this.lineSegmentManagerMap.get(typeName) + if (!lineSegmentManager) { + lineSegmentManager = createFn() + if (lineSegmentManager) { + this.lineSegmentManagerMap.set(typeName, lineSegmentManager) + } + } + return lineSegmentManager + } + + /** * 初始化 THREE 渲染器 */ async initThree(option: InitThreeOption) { @@ -179,6 +211,7 @@ export default class Viewport { statsControls.dom.style.position = 'absolute' statsControls.dom.style.top = '0' statsControls.dom.style.left = '0' + statsControls.dom.style.zIndex = '1' viewerDom.parentElement.parentElement.appendChild(statsControls.dom) $(statsControls.dom).children().css('height', '28px') diff --git a/src/core/manager/EntityManager.ts b/src/core/manager/EntityManager.ts index 47135d1..d4128c7 100644 --- a/src/core/manager/EntityManager.ts +++ b/src/core/manager/EntityManager.ts @@ -96,11 +96,13 @@ export default class EntityManager { /** * 创建或更新一个实体, 这个点的 center[] / in[] / out[] 关联的点, 可能都要对应进行关联 */ - createOrUpdateEntity(entityRaw: ItemJson, option: EntityCudOption = {}): void { - if (!entityRaw?.id) { + createOrUpdateEntity(entity: ItemJson, option: EntityCudOption = {}): void { + // if (!entityRaw?.id) { + if (!entity?.id) { throw new Error('Entity must have an id') } - const entity = _.cloneDeep(entityRaw) as ItemJson + // 改成非深拷贝, 直接使用原始数据, 因为 renderer 可能会修改这个数据, 后续也要能生效 + // const entity = _.cloneDeep(entityRaw) as ItemJson const originEntity = this.___entityMap.get(entity.id) // 找到这个数据的渲染器 @@ -418,6 +420,34 @@ export default class EntityManager { return this.__objectMap.get(id) } + replaceObject(id: string, newObject: Object3DLike) { + if (newObject.userData.entityId !== id) { + throw new Error(`New object must have userData.entityId set to ${id}`) + } + const originalObject = this.__objectMap.get(id) + + _.remove(this._selectableObjects, obj => obj.userData?.entityId === id) + _.remove(this._draggableObjects, obj => obj.userData?.entityId === id) + + this.__objectMap.set(id, newObject) + // 如果是可选中对象,添加到 _selectableObjects 中 + if (newObject instanceof THREE.Object3D && newObject.userData.selectable !== false) { + this._selectableObjects.push(newObject) + } + if (newObject instanceof THREE.Object3D && newObject.userData.draggable) { + this._draggableObjects.push(newObject) + } + + if (this.viewport.state.selectedEntityId === id) { + // 如果当前选中的对象被替换了,更新选中状态 + this.viewport.selectInspect.selectById(id) + } + if (_.includes(this.viewport.state.multiSelectedEntityIds, id)) { + // 如果当前多选的对象被替换了,更新多选状态 + this.viewport.selectInspect.multiSelectByIds(this.viewport.state.multiSelectedEntityIds) + } + } + appendObject(id: string, object: Object3DLike) { this.__objectMap.set(id, object) // 如果是可选中对象,添加到 _selectableObjects 中 diff --git a/src/editor/menus/Model3DView.ts b/src/editor/menus/Model3DView.ts index 7505bd1..7896b5a 100644 --- a/src/editor/menus/Model3DView.ts +++ b/src/editor/menus/Model3DView.ts @@ -1,6 +1,7 @@ import { defineMenu } from '@/runtime/DefineMenu.ts' import Model3DView from '@/components/Model3DView.vue' import ThreePerfView from '@/components/ThreePerfView.vue' +import ThreePerfView2 from '@/components/ThreePerfView2.vue' export default defineMenu((menus) => { menus.insertChildren('tool', @@ -37,6 +38,21 @@ export default defineMenu((menus) => { dialogClass: 'model-3d-view-wrap' }) } + }, + { + name: 'threePerfView2', label: '性能测试2', order: 3, + click: () => { + system.showDialog(ThreePerfView2, { + title: '性能测试2', + width: 950, + height: 400, + showClose: true, + showMax: true, + showCancelButton: false, + showOkButton: false, + dialogClass: 'model-3d-view-wrap' + }) + } } ] ) diff --git a/src/example/example1.js b/src/example/example1.js index 2bb7e27..9138f70 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -323,6 +323,25 @@ export default { }] }, { + catalogCode: 'f3', t: 'floor', + items: [ + { + id: 'gstore3', + t: 'gstore', + v: true, + tf: [[0, 0.1, 0], [0, 0, 0], [2.0, 0.1, 1.0]], + dt: { in: [], out: [], center: [], storeWidth: 1.4, storeDepth: 1.4 } + }, + { + id: 'gstore4', + t: 'gstore', + v: true, + tf: [[5, 0.1, 0], [0, 0, 0], [2.0, 0.1, 1.0]], + dt: { in: [], out: [], center: [], storeWidth: 1.4, storeDepth: 1.4 } + } + ] + }, + { catalogCode: '__f1', t: 'floor', items: buildAgvPerformanceData(100, 100) } @@ -337,6 +356,7 @@ export default { { catalogCode: '__f1', label: '地下室 (-f1)' }, // 目录项 { catalogCode: 'f1', label: '一楼 (f1)' }, { catalogCode: 'f2', label: '二楼 (f2)' }, + { catalogCode: 'f3', label: '三楼 (f3)' }, { catalogCode: 'OUT', label: '外场 (OUT)' }, { catalogCode: 'fe', label: '楼层电梯 (fe)' } ] diff --git a/src/modules/gstore/GstorePropertySetter.ts b/src/modules/gstore/GstorePropertySetter.ts index d12e150..09252ed 100644 --- a/src/modules/gstore/GstorePropertySetter.ts +++ b/src/modules/gstore/GstorePropertySetter.ts @@ -1,12 +1,20 @@ -import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; -import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; +import type { PropertySetter } from '@/core/base/PropertyTypes.ts' +import { basicFieldsSetter } from '@/editor/widgets/property/PropertyPanelConstant.ts' const propertySetter: PropertySetter = { - flatten: { - fields: [ - ...basicFieldsSetter, - ], - }, -}; + flatten: { + fields: [ + ...basicFieldsSetter, + { + dataPath: 'dt.strokeColor', label: '边线颜色', input: 'ColorPicker', + inputProps: {} + }, + { + dataPath: 'dt.strokeWidth', label: '边线宽度', input: 'InputNumber', + inputProps: {} + } + ] + } +} -export default propertySetter; +export default propertySetter diff --git a/src/modules/gstore/GstoreRenderer.ts b/src/modules/gstore/GstoreRenderer.ts index f29076b..e7e5372 100644 --- a/src/modules/gstore/GstoreRenderer.ts +++ b/src/modules/gstore/GstoreRenderer.ts @@ -1,9 +1,12 @@ import * as THREE from 'three' +import * as IfxUtils from '@/core/IfxUtils.ts' 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' +import InstancePointManager from '@/core/manager/InstancePointManager.ts' +import { BasePlane, type Object3DLike } from '@/types/ModelTypes.ts' /** * 地堆货位渲染器 @@ -20,95 +23,120 @@ export default class GstoreRenderer extends BaseRenderer { 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 + readonly defaultPointOption = { + weight: 0.1, + width: 1.5, + depth: 1.3, + strokeColor: 0x038217, + strokeWidth: 0.15 + } - constructor(itemTypeName: string) { - super(itemTypeName) + centerMaterial = new THREE.MeshBasicMaterial({ + color: 0xffffff, + transparent: true, + opacity: 0.5 + }) + + getColorOfMaterial(strokeColor: number): THREE.MeshBasicMaterial { + return new THREE.MeshBasicMaterial({ + color: strokeColor, + transparent: true, + opacity: 0.9 + }) } - /** - * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 - */ - override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) { - super.afterCreateOrUpdatePoint(item, option, object) + edgeMaterial = this.getColorOfMaterial(this.defaultPointOption.strokeColor) + + createPoint(item: ItemJson, option?: RendererCudOption): Object3DLike { + const point = this.createMesh(item.tf[2][0], item.tf[2][2], item.dt.strokeWidth, item.dt.strokeColor) + point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) - const group = object - group.position.y = this.defulePositionY - group.scale.set(item.dt.storeWidth, this.defaultScale.y, item.dt.storeDepth) - group.rotation.set( + point.rotation.set( THREE.MathUtils.degToRad(item.tf[1][0]), THREE.MathUtils.degToRad(item.tf[1][1]), THREE.MathUtils.degToRad(item.tf[1][2]) ) - - // const planeMesh = group.children[0] as THREE.Mesh - // planeMesh.geometry.dispose() - // - // const newGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth) - // planeMesh.geometry = newGeometry + return point } - createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { - throw new Error('not allow store line.') - } + updatePoint(item: ItemJson, object: THREE.Group, option?: RendererCudOption): Object3DLike { + // 如果 object.userData.strokeWidth 没有变化,并且 scale.x/z 没有变化, 则只平移位置 + if (!object || + (object.userData.strokeWidth !== item.dt.strokeWidth && item.dt.strokeWidth) || + (object.userData.strokeColor !== item.dt.strokeColor && item.dt.strokeColor) || + (object.userData.width !== item.tf[2][0] || object.userData.depth !== item.tf[2][2])) { - updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { - throw new Error('not allow store line.') - } + // 线宽度 / 大小 / 颜色发生变化. 重新创建一个新的点 + object = this.createPoint(item) as THREE.Group + } - createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - throw new Error('not allow createPointBasic.') + object.rotation.set( + THREE.MathUtils.degToRad(item.tf[1][0]), + THREE.MathUtils.degToRad(item.tf[1][1]), + THREE.MathUtils.degToRad(item.tf[1][2]) + ) + object.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + return object } - createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - // 创建平面几何体 - if (!item.dt.storeWidth || !item.dt.storeDepth) { - system.showErrorDialog('地堆货位缺少 storeWidth 或 storeDepth 属性') - return null - } - const group = new THREE.Group() - group.name = GstoreRenderer.POINT_NAME - - // 绘制背景矩形框 - const planeGeometry = new THREE.PlaneGeometry(1, 1) - planeGeometry.rotateX(Math.PI / 2) - const planeMaterial = new THREE.MeshBasicMaterial({ - color: '#dee8ee', - transparent: true, // 启用透明 - opacity: 0.5, // 50%透明度 - depthWrite: false, // 防止深度冲突 - side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} + createMesh(totalWidth: number, totalDepth: number, strokeWidth = 0.15, strokeColor = this.defaultPointOption.strokeColor): Object3DLike { + // === 边框部分(使用 Shape + Hole 构建)=== + const shape = new THREE.Shape() + shape.moveTo(-totalWidth / 2, -totalDepth / 2) + shape.lineTo(totalWidth / 2, -totalDepth / 2) + shape.lineTo(totalWidth / 2, totalDepth / 2) + shape.lineTo(-totalWidth / 2, totalDepth / 2) + shape.closePath() + + const hole = new THREE.Path() + hole.moveTo(-totalWidth / 2 + strokeWidth, -totalDepth / 2 + strokeWidth) + hole.lineTo(totalWidth / 2 - strokeWidth, -totalDepth / 2 + strokeWidth) + hole.lineTo(totalWidth / 2 - strokeWidth, totalDepth / 2 - strokeWidth) + hole.lineTo(-totalWidth / 2 + strokeWidth, totalDepth / 2 - strokeWidth) + hole.closePath() + + shape.holes.push(hole) + + // ExtrudeGeometry 默认是沿 Z 轴拉伸 → 我们需要让它朝 Y 轴拉伸 + const edgeGeometry = new THREE.ExtrudeGeometry(shape, { + depth: 0.02, + bevelEnabled: false }) - const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial) - group.add(planeMesh) - - // 绘制边框 - const lineXLen = item.dt.storeWidth - this.defaultLineWidth - const lineYLen = item.dt.storeDepth - 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: '#038217', - 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) + edgeGeometry.rotateX(-Math.PI / 2) + edgeGeometry.translate(0, 0.01, 0) + + const centerGeometry = new THREE.PlaneGeometry(totalWidth, totalDepth) + centerGeometry.rotateX(-Math.PI / 2) - // 设置位置 - group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + // === 网格组合 === + const edgeMesh = new THREE.Mesh(edgeGeometry, + (this.defaultPointOption.strokeColor === strokeColor) ? this.edgeMaterial : this.getColorOfMaterial(strokeColor) + ) + const centerMesh = new THREE.Mesh(centerGeometry, this.centerMaterial) + + const group = new THREE.Group() + group.add(edgeMesh) + if (this.defaultPointOption.strokeColor !== strokeColor) { + // this.centerMaterial.color.set(strokeColor) + edgeMesh.material.color.set(strokeColor) + } + group.add(centerMesh) + group.userData.strokeWidth = strokeWidth + group.userData.strokeColor = strokeColor + group.userData.width = totalWidth + group.userData.depth = totalDepth return group } + 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.') + } + dispose() { super.dispose() this.pointMaterial?.dispose() diff --git a/src/modules/measure/MeasureRenderer.ts b/src/modules/measure/MeasureRenderer.ts index b5d81a8..e1fac1b 100644 --- a/src/modules/measure/MeasureRenderer.ts +++ b/src/modules/measure/MeasureRenderer.ts @@ -11,15 +11,8 @@ import type { Object3DLike } from '@/types/ModelTypes.ts' * 辅助测量工具渲染器 */ export default class MeasureRenderer extends BaseRenderer { - /** - * 当前测绘内容组, 所有测量点、线、标签都在这个组中. 但不包括临时点、线 - */ - group: THREE.Group - - static GROUP_NAME = 'measure_group' static LABEL_NAME = 'measure_label' static POINT_NAME = 'measure_point' - static LINE_NAME = 'measure_line' public useHtmlLabel = false @@ -40,12 +33,28 @@ export default class MeasureRenderer extends BaseRenderer { readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.25, 0.1) readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0) - createPointManager(): InstancePointManager { - return InstancePointManager.create(this.itemTypeName, this.tempViewport, this.pointGeometry, this.pointMaterial, Constract.MAX_MEASURE_INSTANCES) + get pointManager(): InstancePointManager { + if (!this.tempViewport) { + throw new Error('tempViewport is not set.') + } + return this.tempViewport.getOrCreatePointManager(this.itemTypeName, () => + InstancePointManager.create(this.itemTypeName, + this.tempViewport, + this.pointGeometry, + this.pointMaterial, + Constract.MAX_MEASURE_INSTANCES) + ) } - createLineSegmentManager(): LineSegmentManager { - return LineSegmentManager.create(this.itemTypeName, this.tempViewport, this.lineMaterial) + get lineSegmentManager(): LineSegmentManager { + if (!this.tempViewport) { + throw new Error('tempViewport is not set.') + } + return this.tempViewport.getOrCreateLineManager(this.itemTypeName, () => + LineSegmentManager.create(this.itemTypeName, + this.tempViewport, + this.lineMaterial) + ) } /** @@ -64,31 +73,6 @@ export default class MeasureRenderer extends BaseRenderer { ) } - - // createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { - // const geom = new LineGeometry() - // const obj = new Line2(geom, this.lineMaterial) - // obj.frustumCulled = false - // obj.name = MeasureRenderer.LINE_NAME - // obj.uuid = getLineId(start.id, end.id, type) - // - // return obj - // } - // - // createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - // // const tt = new THREE.BoxGeometry(1, 1, 1) - // // const obj = new THREE.Mesh(tt, this.movelinePoint) - // // obj.name = MeasureRenderer.POINT_NAME - // // obj.uuid = item.id - // // - // // return [obj] - // - // // 创建平面几何体 - // // const obj = new THREE.Mesh(this.pointGeometry,this.pointMaterial) - // // obj.name = MeasureRenderer.POINT_NAME - // // return obj - // } - createPointBasic(item: ItemJson, option?: RendererCudOption): Object3DLike { // 不允许改变高度/角度/大小 item.tf = [ @@ -104,33 +88,6 @@ export default class MeasureRenderer extends BaseRenderer { return this.lineSegmentManager.createLine(lineId, start.tf[0], end.tf[0], this.lineMaterial.color) } - // createLine(start: ItemJson, end: ItemJson, type: LinkType): Object3DLike { - // const lineId = getLineId(start.id, end.id, type) - // return this.lineSegmentManager.createLine(lineId, start.tf[0], end.tf[0], this.lineMaterial.color, { - // lineId: lineId, - // startId: start.id, - // endId: end.id - // }) - // } - -// appendToScene(...objects: THREE.Object3D[]) { - // if (!this.group || this.group.parent !== this.tempViewport.scene.scene) { - // if (this.group && this.group.parent !== this.tempViewport.scene.scene) { - // // 幻影加载问题 - // this.group.parent.removeFromParent() - // } - // - // this.group = new THREE.Group() - // this.group.name = MeasureRenderer.GROUP_NAME - // this.tempViewport?.scene.add(this.group) - // } - // - // const dragObjects = objects.filter(obj => !!obj.userData.draggable) - // //this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') - // - // this.group.add(...objects) - // } - afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) { super.afterCreateOrUpdateLine(start, end, type, option, object) @@ -142,54 +99,15 @@ export default class MeasureRenderer extends BaseRenderer { fontSize: 0.4, color: '#333333' }) - - // const p0 = startPoint.position - // const p1 = endPoint.position - // - // const dist = p0.distanceTo(p1) - // const label = numberToString(dist) + ' m' - // - // const position = new THREE.Vector3().addVectors(p0, p1).multiplyScalar(0.5) - // let labelObj: Text | CSS2DObject | undefined = object.userData.labelObj - // if (!labelObj || !labelObj.parent) { - // labelObj = this.createLabel(label) - // this.appendToScene(labelObj) - // object.userData.labelObj = labelObj - // } - // - // labelObj.position.set(position.x, position.y + 0.3, position.z - 0.2) - // - // if (this.useHtmlLabel) { - // labelObj.element.innerHTML = label - // - // } else { - // // 让文本朝向摄像机 - // labelObj.quaternion.copy(this.tempViewport.camera.quaternion) - // labelObj.text = label - // labelObj.sync() - // } } afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) { super.afterDeleteLine(start, end, type, option, object) - this.tempViewport.labelManager.removeLabel(object) - // 删除标签 - // const labelObj = object.userData?.labelObj - // if (labelObj && labelObj.parent) { - // labelObj.parent.remove(labelObj) - // } } - dispose() { super.dispose() - - if (this.group && this.group.parent) { - this.group.parent.remove(this.group) - } - this.group = undefined - this.pointMaterial?.dispose() this.lineMaterial?.dispose() } diff --git a/src/types/ModelTypes.ts b/src/types/ModelTypes.ts index f4b6f69..aab431d 100644 --- a/src/types/ModelTypes.ts +++ b/src/types/ModelTypes.ts @@ -38,6 +38,24 @@ import * as THREE from 'three' // end: THREE.Vector3; // } +/** + * 平面的基本定义 + */ +export const BasePlane = { + LEFT: 0b000010, + RIGHT: 0b000001, + FRONT: 0b001000, + BEHIND: 0b000100, + TOP: 0b100000, + BOTTOM: 0b010000, + TRANSVERSE: 0b10000000, + LONGITUDINAL: 0b01000000, + THROW: 0b00100000, + toArray: () => { + return [BasePlane.LEFT, BasePlane.BEHIND, BasePlane.RIGHT, BasePlane.FRONT, BasePlane.BOTTOM, BasePlane.TOP, BasePlane.TRANSVERSE, BasePlane.LONGITUDINAL, BasePlane.THROW] + } +} + export interface LineManageReference { manager: LineSegmentManager id: string