From 07a095ee32a516e733d742c6351d7cc49bb3fe64 Mon Sep 17 00:00:00 2001 From: yvan Date: Fri, 30 May 2025 02:01:47 +0800 Subject: [PATCH] =?UTF-8?q?SelectInspect=20=E9=80=89=E6=8B=A9=E5=B7=A5?= =?UTF-8?q?=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/designer/model2DEditor/tools/SelectInspect.ts | 143 ++++++++++++++++++++-- 1 file changed, 131 insertions(+), 12 deletions(-) diff --git a/src/designer/model2DEditor/tools/SelectInspect.ts b/src/designer/model2DEditor/tools/SelectInspect.ts index c57264e..36c93ec 100644 --- a/src/designer/model2DEditor/tools/SelectInspect.ts +++ b/src/designer/model2DEditor/tools/SelectInspect.ts @@ -6,30 +6,63 @@ 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' +let pdFn, pmFn, puFn + /** * 选择工具,用于在设计器中显示选中对象的包围盒 */ export default class SelectInspect implements ITool { - viewport: any - material: LineMaterial + viewport: Viewport + /** + * 线框材质,用于显示选中对象的包围盒 + */ + material: LineMaterial = new LineMaterial({ color: 0xffff00, linewidth: 2 }) + + /** + * 矩形材质,用于显示鼠标拖拽选择的矩形区域 + */ + rectMaterial: THREE.MeshBasicMaterial = new THREE.MeshBasicMaterial({ + color: 0x000000, + opacity: 0.5, + transparent: true + }) + + /** + * 当前选中对象的矩形选择框 + */ + rectangle: THREE.Mesh | null = null + + /** + * 当前选中对象的包围盒线框 + */ selectionBox: Line2 + /** + * 当前鼠标所在的画布, 对应 viewport.renderer.domElement + */ + canvas: HTMLCanvasElement + + /** + * 鼠标按下时记录的起始位置,用于绘制矩形选择框 + */ + recStartPos: THREE.Vector3 | null + constructor() { - // 颜色变成黄 - const color = 0xffff00 // 0xff0000 - const lineWidth = 2 - this.material = new LineMaterial({ color, linewidth: lineWidth }) } init(viewport: Viewport) { this.viewport = viewport + this.canvas = this.viewport.renderer.domElement as HTMLCanvasElement + + pdFn = this.onMouseDown.bind(this) + this.canvas.addEventListener('pointerdown', pdFn) + pmFn = this.onMouseMove.bind(this) + this.canvas.addEventListener('pointermove', pmFn) + puFn = this.onMouseUp.bind(this) + this.canvas.addEventListener('pointerup', puFn) 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 - } + this.disposeSelectionBox() const expandAmount = 0.2 // 扩展包围盒的大小 if (selectedObject !== null) { @@ -66,11 +99,97 @@ export default class SelectInspect implements ITool { } destory() { + + this.canvas.removeEventListener('pointerdown', pdFn) + pdFn = undefined + this.canvas.removeEventListener('pointermove', pmFn) + pmFn = undefined + this.canvas.removeEventListener('pointerup', puFn) + puFn = undefined + // 销毁选择工具 + this.disposeSelectionBox() + this.disposeRect() + } + + disposeSelectionBox() { if (this.selectionBox) { this.viewport.scene.remove(this.selectionBox) this.selectionBox.geometry.dispose() this.selectionBox = null } } -} \ No newline at end of file + + createRectangle() { + if (this.rectangle !== null) { + this.disposeRect() + } + if (this.recStartPos) { + // 创建矩形 + this.rectangle = new THREE.Mesh( + new THREE.PlaneGeometry(1, 1), + this.rectMaterial + ) + this.rectangle.name = 'selectRectangle' + this.rectangle.rotation.x = -Math.PI / 2 // 关键!让平面正对相机 + this.rectangle.position.set( + this.recStartPos.x, + this.recStartPos.y, + this.recStartPos.z + ) + this.viewport.scene.add(this.rectangle) + } + } + + updateRectangle(position: THREE.Vector3) { + if (!this.rectangle || !this.recStartPos) return + // console.log('updateRectangle', this.recStartPos, position) + + const width = position.x - this.recStartPos.x + const height = position.z - this.recStartPos.z + + const newWidth = Math.abs(width) + const newHeight = Math.abs(height) + + // 清理旧几何体 + this.rectangle.geometry.dispose() + this.rectangle.geometry = new THREE.PlaneGeometry(newWidth, newHeight) + this.rectangle.position.set( + this.recStartPos.x + width / 2, + this.recStartPos.y, + this.recStartPos.z + height / 2 + ) + } + + onMouseDown(event: MouseEvent) { + if (event.shiftKey) { + // 记录鼠标按下位置 + this.recStartPos = this.viewport.getClosestIntersection(event) + this.createRectangle() + } + } + + + onMouseMove(event: MouseEvent) { + if (!this.recStartPos) { + this.disposeRect() + } + // 更新矩形大小或重新生成矩形 + const position = this.viewport.getClosestIntersection(event) + if (!position) return + this.updateRectangle(position) + } + + disposeRect() { + if (this.rectangle !== null) { + this.viewport.scene.remove(this.rectangle) + this.rectangle.geometry.dispose() + this.rectangle = null + } + this.recStartPos = null + } + + onMouseUp(event: MouseEvent) { + this.disposeRect() + } +}