diff --git a/src/designer/Viewport.ts b/src/designer/Viewport.ts index 5525ab9..05f1483 100644 --- a/src/designer/Viewport.ts +++ b/src/designer/Viewport.ts @@ -6,18 +6,16 @@ import Stats from 'three/examples/jsm/libs/stats.module' import type WorldModel from '@/model/WorldModel.ts' import $ from 'jquery' import { reactive, watch } from 'vue' -import MouseMoveInspect from '@/designer/model2DEditor/tools/MouseMoveInspect.ts' import type { ITool } from '@/designer/model2DEditor/tools/ITool.ts' import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer' import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer' import { getAllItemTypes } from '@/runtime/DefineItemType.ts' import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts' + import type Toolbox from '@/model/itemType/Toolbox.ts' import { calcPositionUseSnap } from '@/model/ModelUtils.ts' -import textureUrl from '@/assets/images/conveyor/shapes/Belt1.png' -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 SelectInspect from '@/designer/model2DEditor/tools/SelectInspect.ts' +import MouseMoveInspect from '@/designer/model2DEditor/tools/MouseMoveInspect.ts' /** * 编辑器对象 @@ -41,7 +39,8 @@ export default class Viewport { toolStartObject: THREE.Object3D | null = null currentTool: Toolbox | null = null tools: ITool[] = [ - new MouseMoveInspect() + new MouseMoveInspect(), + new SelectInspect() ] toolbox: Record = {} @@ -63,6 +62,7 @@ export default class Viewport { currentFloor: '', isReady: false, cursorMode: 'normal', + selectedObject: null, camera: { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 } @@ -209,35 +209,8 @@ export default class Viewport { } this.updateGridVisibility() })) - this.watchList.push(watch(() => this.state.cursorMode, (newVal: CursorMode) => { - if (!this.state.isReady) { - return - } - if (this.currentTool) { - this.currentTool.stop() - this.currentTool = null - } - if (newVal === 'normal' || !newVal) { - this.dragControl.dragControls.enabled = true - return - } - - const currentTool = this.toolbox[newVal] - if (currentTool) { - // 选择标尺工具 - this.currentTool = currentTool - this.dragControl.dragControls.enabled = false - - } else { - system.showErrorDialog(`当前鼠标模式 ${newVal} 不支持`) - } - - if (this.currentTool) { - this.currentTool.start(this.toolStartObject) - this.toolStartObject = null - } - })) + // 监听窗口大小变化 if (this.resizeObserver) { this.resizeObserver.unobserve(this.viewerDom) } @@ -252,7 +225,7 @@ export default class Viewport { tool.init(this) } - + // 触发所有物品类型的 afterAddViewport 方法 _.forEach(getAllItemTypes(), (itemType: ItemTypeDefineOption) => { itemType.clazz.afterAddViewport(this) }) @@ -495,6 +468,11 @@ export interface ViewportState { cursorMode: CursorMode, /** + * 选中的对象 + */ + selectedObject: THREE.Object3D | null + + /** * 相机状态 */ camera: { diff --git a/src/designer/model2DEditor/EsDragControls.ts b/src/designer/model2DEditor/EsDragControls.ts index 24cfc12..1824455 100644 --- a/src/designer/model2DEditor/EsDragControls.ts +++ b/src/designer/model2DEditor/EsDragControls.ts @@ -4,6 +4,7 @@ import type Viewport from '@/designer/Viewport.ts' import Constract from '@/designer/Constract.ts' import { getItemTypeByName } from '@/runtime/DefineItemType.ts' import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts' +import { markRaw } from 'vue' // dragControls 绑定函数 let dragStartFn, dragFn, dragEndFn, clickblankFn @@ -114,6 +115,9 @@ export default class EsDragControls { if (e.object.userData.onClick) { e.object.userData.onClick(e) } + if (e.object.userData.selectable) { + this.viewport.state.selectedObject = markRaw(e.object) + } } if (e.object.userData?.type) { diff --git a/src/designer/model2DEditor/tools/ITool.ts b/src/designer/model2DEditor/tools/ITool.ts index 0130e4c..20806e0 100644 --- a/src/designer/model2DEditor/tools/ITool.ts +++ b/src/designer/model2DEditor/tools/ITool.ts @@ -1,5 +1,7 @@ -export interface ITool { +export interface ITool { init(viewport: any): void destory(): void + + animate?: () => void; } \ No newline at end of file diff --git a/src/designer/model2DEditor/tools/MouseMoveInspect.ts b/src/designer/model2DEditor/tools/MouseMoveInspect.ts index bd03d9a..fc539be 100644 --- a/src/designer/model2DEditor/tools/MouseMoveInspect.ts +++ b/src/designer/model2DEditor/tools/MouseMoveInspect.ts @@ -70,4 +70,7 @@ export default class MouseMoveInspect implements ITool { } }, 1) + + animate(): void { + } } \ No newline at end of file diff --git a/src/designer/model2DEditor/tools/SelectInspect.ts b/src/designer/model2DEditor/tools/SelectInspect.ts new file mode 100644 index 0000000..c57264e --- /dev/null +++ b/src/designer/model2DEditor/tools/SelectInspect.ts @@ -0,0 +1,76 @@ +import * as THREE from 'three' +import type { ITool } from './ITool.ts' +import { watch } from 'vue' +import type Viewport from '@/designer/Viewport.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 SelectInspect implements ITool { + viewport: any + material: LineMaterial + selectionBox: Line2 + + constructor() { + // 颜色变成黄 + const color = 0xffff00 // 0xff0000 + const lineWidth = 2 + this.material = new LineMaterial({ color, linewidth: lineWidth }) + } + + init(viewport: Viewport) { + this.viewport = viewport + + this.viewport.watchList.push(watch(() => this.viewport.state.selectedObject, (selectedObject) => { + if (this.selectionBox) { + viewport.scene.remove(this.selectionBox) + this.selectionBox.geometry.dispose() + this.selectionBox = null + } + + const expandAmount = 0.2 // 扩展包围盒的大小 + if (selectedObject !== null) { + // 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb) + selectedObject.updateWorldMatrix(false, true) + + const box = new THREE.Box3().setFromObject(selectedObject) + box.expandByScalar(expandAmount) + + const size = new THREE.Vector3() + box.getSize(size) + + const center = new THREE.Vector3() + box.getCenter(center) + + // 创建包围盒几何体 + const helperGeometry = new THREE.BoxGeometry(size.x, size.y, size.z) + const edgesGeometry = new THREE.EdgesGeometry(helperGeometry) + + // 使用 LineGeometry 包装 edgesGeometry + const lineGeom = new LineGeometry() + //@ts-ignore + lineGeom.setPositions(edgesGeometry.attributes.position.array) + + const selectionBox = new Line2(lineGeom, this.material) + selectionBox.computeLineDistances() + selectionBox.position.copy(center) + selectionBox.name = 'selectionBox' + this.selectionBox = selectionBox + + this.viewport.scene.add(selectionBox) + } + })) + } + + destory() { + // 销毁选择工具 + if (this.selectionBox) { + this.viewport.scene.remove(this.selectionBox) + this.selectionBox.geometry.dispose() + this.selectionBox = null + } + } +} \ No newline at end of file diff --git a/src/model/itemType/ItemTypeLine.ts b/src/model/itemType/ItemTypeLine.ts index 57cc6a5..bbdd634 100644 --- a/src/model/itemType/ItemTypeLine.ts +++ b/src/model/itemType/ItemTypeLine.ts @@ -105,10 +105,13 @@ export default abstract class ItemTypeLine extends ItemType { point.name = item.name point.uuid = item.id || THREE.MathUtils.generateUUID() point.userData = _.cloneDeep(item.dt) || {} - point.userData.type = item.t - point.userData.actionType = item.a - point.userData.label = item.l - point.userData.color = item.c + _.extend(point.userData, { + type: item.t, + actionType: item.a, + label: item.l, + color: item.c, + selectable: true + }) point.rotation.set( THREE.MathUtils.degToRad(item.tf[1][0]),