From 14851168989bcbf95a29ac396fa274cbc7471efa Mon Sep 17 00:00:00 2001 From: yvan Date: Sun, 25 May 2025 23:57:18 +0800 Subject: [PATCH] =?UTF-8?q?Measure=20=E6=B5=8B=E9=87=8F=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/designer/Constract.ts | 23 ++ src/designer/Viewport.ts | 24 +- src/designer/model2DEditor/EsDragControls.ts | 21 +- src/designer/model2DEditor/Model2DEditor.vue | 20 +- src/designer/model2DEditor/Model2DEditorJs.js | 2 + src/designer/model2DEditor/tools/CursorTool.ts | 17 +- src/designer/model2DEditor/tools/MeasureTool.ts | 438 ++++++++++++++++++++++++ src/designer/model2DEditor/tools/RulerTool.ts | 359 ------------------- src/types/Types.d.ts | 14 +- src/utils/webutils.ts | 6 +- 10 files changed, 527 insertions(+), 397 deletions(-) create mode 100644 src/designer/Constract.ts create mode 100644 src/designer/model2DEditor/tools/MeasureTool.ts delete mode 100644 src/designer/model2DEditor/tools/RulerTool.ts diff --git a/src/designer/Constract.ts b/src/designer/Constract.ts new file mode 100644 index 0000000..444e7ac --- /dev/null +++ b/src/designer/Constract.ts @@ -0,0 +1,23 @@ +export default Object.freeze({ + // 光标相关 + CursorModeNormal: 'normal', + + // 关联相关 + CursorModeALink: 'ALink', + CursorModeSLink: 'SLink', + CursorModePointCallback: 'PointCallback', + CursorModePointAdd: 'PointAdd', + CursorModeLinkAdd: 'LinkAdd', + CursorModeLinkAdd2: 'LinkAdd2', + + // 测量相关的光标模式 + CursorModeMeasure: 'Measure', + CursorModeMeasureArea: 'MeasureArea', + CursorModeMeasureAngle: 'MeasureAngle', + MeasureMarker: 'measure-marker', + MeasureLine: 'measure-line', + MeasureLabel: 'measure-label', + + // 选择模式 + CursorModeSelectByRec: 'selectByRec' +}) \ No newline at end of file diff --git a/src/designer/Viewport.ts b/src/designer/Viewport.ts index eb2f360..6b40795 100644 --- a/src/designer/Viewport.ts +++ b/src/designer/Viewport.ts @@ -9,9 +9,10 @@ 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 RulerTool from '@/designer/model2DEditor/tools/RulerTool.ts' +import MeasureTool from '@/designer/model2DEditor/tools/MeasureTool.ts' import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer' import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer' +import type { ICursorTool } from '@/designer/model2DEditor/tools/CursorTool.ts' /** * 编辑器对象 @@ -29,9 +30,10 @@ export default class Viewport { worldModel: WorldModel raycaster: Raycaster dragControl: EsDragControls + measure: MeasureTool animationFrameId: any = null - currentTool: ITool | null = null + currentTool: ICursorTool | null = null tools: ITool[] = [ new MouseMoveInspect() ] @@ -80,7 +82,7 @@ export default class Viewport { * 初始化 THREE 渲染器 */ initThree(viewerDom: HTMLElement, floor: string) { - console.log('init floor', floor) + console.log('viewport on floor', floor) this.state.currentFloor = floor this.viewerDom = viewerDom const rect = viewerDom.getBoundingClientRect() @@ -105,7 +107,7 @@ export default class Viewport { renderer.clearDepth() renderer.shadowMap.enabled = true renderer.toneMapping = THREE.ACESFilmicToneMapping - renderer.setPixelRatio(Math.max(Math.ceil(window.devicePixelRatio), 1)); + renderer.setPixelRatio(Math.max(Math.ceil(window.devicePixelRatio), 1)) renderer.setViewport(0, 0, this.viewerDom.offsetWidth, this.viewerDom.offsetHeight) renderer.setSize(this.viewerDom.offsetWidth, this.viewerDom.offsetHeight) @@ -195,12 +197,12 @@ export default class Viewport { } this.updateGridVisibility() })) - this.watchList.push(watch(() => this.state.cursorMode, (newVal) => { + this.watchList.push(watch(() => this.state.cursorMode, (newVal: CursorMode) => { if (!this.state.isReady) { return } if (this.currentTool) { - this.currentTool.destory() + this.currentTool.stop() this.currentTool = null } if (newVal === 'normal' || !newVal) { @@ -208,9 +210,9 @@ export default class Viewport { return } - if (newVal === 'Ruler' || newVal === 'RulerArea' || newVal === 'RulerAngle') { + if (newVal === 'Measure' || newVal === 'MeasureArea' || newVal === 'MeasureAngle') { // 选择标尺工具 - this.currentTool = new RulerTool() + this.currentTool = this.measure this.dragControl.dragControls.enabled = false } else { @@ -218,7 +220,7 @@ export default class Viewport { } if (this.currentTool) { - this.currentTool.init(this) + this.currentTool.start() } })) @@ -239,6 +241,10 @@ export default class Viewport { // 注册拖拽组件 this.dragControl = new EsDragControls(this) + // 注册测量工具 + this.measure = new MeasureTool() + this.measure.init(this) + this.state.isReady = true } diff --git a/src/designer/model2DEditor/EsDragControls.ts b/src/designer/model2DEditor/EsDragControls.ts index cf0e12d..2d9db26 100644 --- a/src/designer/model2DEditor/EsDragControls.ts +++ b/src/designer/model2DEditor/EsDragControls.ts @@ -1,6 +1,7 @@ import { DragControls } from './DragControls.js' import * as THREE from 'three' import type Viewport from '@/designer/Viewport.ts' +import Constract from '@/designer/Constract.ts' // dragControls 绑定函数 let dragStartFn, dragFn, dragEndFn, clickblankFn @@ -76,11 +77,11 @@ export default class EsDragControls { // 记录拖拽按下的位置和对象 this.onDownPosition = { x: e.e.clientX, y: e.e.clientY } - // switch (e.object.userData.type) { - // case 'measure-marker': - // this.viewport.modules.measure.redraw(e.object) - // break - // } + switch (e.object.userData.type) { + case Constract.MeasureMarker: + this.viewport.measure.redraw(e.object) + break + } } // 拖拽中 @@ -107,11 +108,11 @@ export default class EsDragControls { } } - // switch (e.object.userData.type) { - // case 'measure-marker': - // this.viewport.modules.measure.redrawComplete() - // break - // } + switch (e.object.userData.type) { + case 'measure-marker': + this.viewport.measure.redrawComplete() + break + } } // 点击可拖拽物体之外 diff --git a/src/designer/model2DEditor/Model2DEditor.vue b/src/designer/model2DEditor/Model2DEditor.vue index f3e952e..c4c5999 100644 --- a/src/designer/model2DEditor/Model2DEditor.vue +++ b/src/designer/model2DEditor/Model2DEditor.vue @@ -15,28 +15,28 @@
+ :type="state.cursorMode===Constract.CursorModeNormal?'primary':''" + @click="()=>state.cursorMode = Constract.CursorModeNormal"> + :type="state.cursorMode===Constract.CursorModeSelectByRec?'primary':''" + @click="()=>state.cursorMode = Constract.CursorModeSelectByRec"> + :type="state.cursorMode===Constract.CursorModeALink?'primary':''" + @click="()=>state.cursorMode = Constract.CursorModeALink"> + :type="state.cursorMode===Constract.CursorModeSLink?'primary':''" + @click="()=>state.cursorMode = Constract.CursorModeSLink"> + :type="state.cursorMode===Constract.CursorModeMeasure?'primary':''" + @click="()=>state.cursorMode = Constract.CursorModeMeasure">
0) { + // 获取最后一个点 + const p0 = this.pointArray[this.pointArray.length - 1] + if (!this.tempLine) { + this.tempLine = this.createLine() + this.viewport.scene.add(this.tempLine) + } + + const line = this.tempLine + const geom = line.geometry + const startPoint = this.pointArray[0] + const lastPoint = this.pointArray[this.pointArray.length - 1] + if (this.viewport.state.cursorMode === Constract.CursorModeMeasureArea) { + geom.setFromPoints([lastPoint, point, startPoint]) + } else { + geom.setFromPoints([lastPoint, point]) + } + + if (this.viewport.state.cursorMode === Constract.CursorModeMeasure) { + const dist = p0.distanceTo(point) + const label = `${numberToString(dist)} ${getUnitString(this.viewport.state.cursorMode)}` + const position = new THREE.Vector3((point.x + p0.x) / 2, (point.y + p0.y) / 2, (point.z + p0.z) / 2) + this.addOrUpdateTempLabel(label, position) + } + } + + // this.viewport.dispatchSignal('sceneGraphChanged') + } + + mouseup(e: MouseEvent) { + // 如果mouseMoved是true,那么它可能在移动,而不是点击 + if (!this.mouseMoved) { + + if (e.button === 2) { + // 右键点击, 完成绘图操作 + this.viewport.state.cursorMode = 'normal' + + + } else if (e.button === 0) { + // 左键点击, 添加点 + this.onMouseClicked(e) + } + } + } + + onMouseClicked(e: MouseEvent) { + if (this.isCompleted) { + return + } + + // 获取鼠标点击位置的三维坐标 + const point = this.getClosestIntersection(e) + if (!point) { + return + } + + // 双击触发两次点击事件,我们需要避免这里的第二次点击 + const now = Date.now() + if (this.lastClickTime && (now - this.lastClickTime < 50)) { + return + } + this.lastClickTime = now + + // 添加正式点 + this.pointArray.push(point) + + const count = this.pointArray.length + const marker = this.createPointMarker(point) + marker.userData.point = point + marker.userData.pointIndex = count - 1 + this.group.add(marker) + + // 把点加入拖拽控制器 + this.viewport.dragControl.setDragObjects([marker], 'push') + + if (this.tempLabel && count > 1) { + // 临时 点/label/line 将作为正式的使用 + const p0 = this.pointArray[count - 2] + + this.tempLabel.position.set((p0.x + point.x) / 2, (p0.y + point.y) / 2, (p0.z + point.z) / 2) + this.group.add(this.tempLabel) + this.tempLabel = undefined + + // 临时线 + this.tempLine.geometry.setFromPoints([p0, point]) + this.group.add(this.tempLine) + this.tempLine = undefined + } + + // this.viewport.dispatchSignal('sceneGraphChanged') + } + + /** + * Creates THREE.Line + */ + private createLine(): THREE.Line { + const geom = new THREE.BufferGeometry() + const obj = new THREE.Line(geom, MeasureTool.LINE_MATERIAL) + obj.frustumCulled = false + obj.name = MeasureTool.LINE_NAME + obj.userData = { + type: Constract.MeasureLine + } + return obj + } + + /** + * 创建点标记 + */ + createPointMarker(position?: THREE.Vector3): THREE.Mesh { + const p = position + const scale = 0.25 + + const tt = new THREE.BoxGeometry(1, 1, 1) + const t2 = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 }) + const obj = new THREE.Mesh(tt, t2) + obj.scale.set(scale, 0.1, scale) + if (p) { + obj.position.set(p.x, p.y, p.z) + } + + obj.name = MeasureTool.OBJ_NAME + + obj.userData = { + mode: this.viewport.state.cursorMode, + type: Constract.MeasureMarker + } + return obj + } + + + /** + * 获取按下对应三维位置 + */ + getClosestIntersection(e: MouseEvent) { + const _point = new THREE.Vector2() + _point.x = e.offsetX / this.viewport.renderer.domElement.offsetWidth + _point.y = e.offsetY / this.viewport.renderer.domElement.offsetHeight + + const intersects = this.viewport.getIntersects(_point) + if (intersects && intersects.length > 2) { + if (intersects.length > 0 && intersects[0].distance < MeasureTool.MAX_DISTANCE) { + return new Vector3( + intersects[0].point.x, + 0.1, + intersects[1].point.z + ) + } + } + return null + } + + /** + * 添加或更新临时标签和位置 + */ + addOrUpdateTempLabel(label: string, position: THREE.Vector3) { + if (!this.tempLabel) { + this.tempLabel = this.createLabel(label) + console.log('addOrUpdateTempLabel', label, position) + this.viewport.scene.add(this.tempLabel) + } + this.tempLabel.position.set(position.x, position.y, position.z) + this.tempLabel.element.innerHTML = label + } + + + /** + * 创建标签 + */ + createLabel(text: string): CSS2DObject { + const div = document.createElement('div') + div.className = 'css2dObjectLabel' + div.innerHTML = text + div.style.padding = '5px 8px' + div.style.color = '#fff' + div.style.fontSize = '14px' + div.style.position = 'absolute' + div.style.backgroundColor = 'rgba(25, 25, 25, 0.3)' + div.style.borderRadius = '12px' + div.style.top = '0px' + div.style.left = '0px' + // div.style.pointerEvents = 'none' //避免HTML元素影响场景的鼠标事件 + const obj = new CSS2DObject(div) + obj.name = MeasureTool.LABEL_NAME + obj.userData = { + type: Constract.MeasureLabel + } + return obj + } + + + /** + * 拖拽测量点, 重绘 + */ + redraw(point: THREE.Mesh) { + // 当次绘制点 + this.pointArray = []; + + (point.parent as THREE.Group).children.forEach(child => { + switch (child.userData.type) { + case Constract.MeasureMarker: + // 当前点正在操作,不加入 + if (child.uuid !== point.uuid) { + this.pointArray[child.userData.pointIndex] = child.userData.point + } else { + this.tempPointMarker = child as THREE.Mesh + } + break + case Constract.MeasureLine: + this.tempLine = this.createLine() + this.group.add(this.tempLine) + break + } + }) + + // 重写move事件 + pmFn = this.redrawMousemove.bind(this) + this.canvas.addEventListener('pointermove', pmFn) + + this.group = point.parent as THREE.Group + this.isCompleted = false + } + + + /** + * 重绘监听鼠标移动 + */ + redrawMousemove(e: MouseEvent) { + let point = this.getClosestIntersection(e) + + if (!point && this.tempPointMarker) { + this.tempPointMarker.position.set(this.tempPointMarker.userData.point.x, this.tempPointMarker.userData.point.y, this.tempPointMarker.userData.point.z) + return + } + + if (!point || !this.tempPointMarker) return + + // 在鼠标移动时绘制临时点 + this.tempPointMarker.position.set(point.x, point.y, point.z) + this.tempPointMarker.userData.point = point + + // 当前点的索引 + const cIndex = this.tempPointMarker.userData.pointIndex + + // 移动时绘制临时线 + if (this.pointArray.length > 0) { + const line = this.tempLine || this.createLine() + const geom = line.geometry + let startPoint = this.pointArray[cIndex + 1] + let lastPoint = this.pointArray[cIndex - 1] + + if (startPoint && lastPoint) { + geom.setFromPoints([lastPoint, point, startPoint]) + } else { + geom.setFromPoints([startPoint || lastPoint, point]) + } + } + } + + /** + * 重绘完成 + */ + redrawComplete() { + if (!this.tempPointMarker) return + + const point = this.tempPointMarker.userData.point + this.pointArray[this.tempPointMarker.userData.pointIndex] = point + const count = this.pointArray.length + + // 如果是距离测量,则清除group中已有的label,再重新创建 + if (this.viewport.state.cursorMode === 'Measure' && count > 1) { + this.clearCurrentLabel() + // 绘制label + for (let i = 0; i < count - 1; i++) { + const p0 = this.pointArray[i] + const p1 = this.pointArray[i + 1] + if (!p0 || !p1) continue + const dist = p0.distanceTo(p1) + const label = `${numberToString(dist)} ${getUnitString(this.viewport.state.cursorMode)}` + const position = new THREE.Vector3((p0.x + p1.x) / 2, (p0.y + p1.y) / 2, (p0.z + p1.z) / 2) + const labelObj = this.createLabel(label) + labelObj.position.set(position.x, position.y, position.z) + labelObj.element.innerHTML = label + this.group.add(labelObj) + } + } + + // this.destory() + } + + /** + * 清除当前group label + */ + clearCurrentLabel() { + for (let i = this.group.children.length - 1; i >= 0; i--) { + const c = this.group.children[i] + if (c.userData.type === 'label') { + this.group.remove(c) + } + } + } +} \ No newline at end of file diff --git a/src/designer/model2DEditor/tools/RulerTool.ts b/src/designer/model2DEditor/tools/RulerTool.ts deleted file mode 100644 index 23ffd6f..0000000 --- a/src/designer/model2DEditor/tools/RulerTool.ts +++ /dev/null @@ -1,359 +0,0 @@ -import _ from 'lodash' -import PointPng from '@/assets/images/logo.png' -import * as THREE from 'three' -import type { ITool } from '@/designer/model2DEditor/tools/ITool.ts' -import Viewport from '@/designer/Viewport.ts' -import { numberToString, getUnitString } from '@/utils/webutils' -import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' -import { Vector3 } from 'three' - -let pdFn, pmFn, puFn, kdFn - -/** - * 用于在 threejs 中创建一系列的 object_for_measure 点,并标记他的距离 - */ -export default class RulerTool implements ITool { - static OBJ_NAME = 'object_for_measure' - static LABEL_NAME = 'label_for_measure' - static MAX_DISTANCE = 500 //当相交物体的距离太远时,忽略它 - - viewport: Viewport - // 当前测绘内容组 - group: THREE.Group - isCompleted = false - mouseMoved = false - canvas: HTMLCanvasElement - - // 用户在测量时绘制的线的当前实例 - protected polyline?: THREE.Line - - // 用于存储临时点 - protected tempPointMarker?: THREE.Mesh - - // 用于存储临时线条,用于在鼠标移动时绘制线条/区域/角度 - protected tempLine?: THREE.Line - - // 用于在鼠标移动时存储临时标签,只有测量距离时才有 - protected tempLabel?: CSS2DObject - - // 存储点 - protected pointArray: THREE.Vector3[] = [] - - //保存上次点击时间,以便检测双击事件 - protected lastClickTime: number = 0 - - - static LINE_MATERIAL = new THREE.LineBasicMaterial({ - color: 0xE63C17, - linewidth: 2, - opacity: 0.9, - transparent: true, - side: THREE.DoubleSide, - depthWrite: false, - depthTest: false - }) - - init(viewport: Viewport) { - this.viewport = viewport - const viewerDom = this.viewport.viewerDom - this.canvas = $(viewerDom).children('canvas')[0] as HTMLCanvasElement - - pdFn = this.mousedown.bind(this) - this.canvas.addEventListener('pointerdown', pdFn) - pmFn = this.mousemove.bind(this) - this.canvas.addEventListener('pointermove', pmFn) - puFn = this.mouseup.bind(this) - this.canvas.addEventListener('pointerup', puFn) - - // 初始化group - this.group = new THREE.Group() - this.group.name = `${RulerTool.OBJ_NAME}_group` - this.group.userData = { - mode: this.viewport.state.cursorMode - } - this.viewport.scene.add(this.group) - - // 测量距离、面积和角度需要折线 - this.polyline = this.createLine() - this.group.add(this.polyline) - this.isCompleted = false - this.viewport.viewerDom.style.cursor = 'crosshair' - - // 当次绘制点 - this.pointArray = [] - - system.msg('进入鼠标测距模式') - } - - - destory(): void { - system.msg('退出鼠标测距模式') - - const viewerDom = this.viewport.viewerDom - - this.isCompleted = true - viewerDom.style.cursor = '' - - this.canvas.removeEventListener('pointerdown', pdFn) - pdFn = undefined - this.canvas.removeEventListener('pointermove', pmFn) - pmFn = undefined - this.canvas.removeEventListener('pointerup', puFn) - puFn = undefined - - this.tempPointMarker && this.viewport.scene.remove(this.tempPointMarker) - this.tempLine && this.viewport.scene.remove(this.tempLine) - this.tempLabel && this.viewport.scene.remove(this.tempLabel) - this.tempPointMarker = undefined - this.tempLine = undefined - this.tempLabel = undefined - } - - mousedown = () => { - this.mouseMoved = false - } - - // 鼠标移动,创建对应的临时点与线 - mousemove = (e: MouseEvent) => { - if (this.isCompleted) return - - this.mouseMoved = true - - const point = this.getClosestIntersection(e) - if (!point) { - return - } - - // 在鼠标移动时绘制临时点 - if (this.tempPointMarker) { - this.tempPointMarker.position.set(point.x, point.y, point.z) - } else { - this.tempPointMarker = this.createPointMarker(point) - this.viewport.scene.add(this.tempPointMarker) - } - - // 移动时绘制临时线 - if (this.pointArray.length > 0) { - const p0 = this.pointArray[this.pointArray.length - 1] // 获取最后一个点 - const line = this.tempLine || this.createLine() - const geom = line.geometry - const startPoint = this.pointArray[0] - const lastPoint = this.pointArray[this.pointArray.length - 1] - if (this.viewport.state.cursorMode === 'RulerArea') { - geom.setFromPoints([lastPoint, point, startPoint]) - } else { - geom.setFromPoints([lastPoint, point]) - } - if (this.viewport.state.cursorMode === 'Ruler') { - const dist = p0.distanceTo(point) - const label = `${numberToString(dist)} ${getUnitString(this.viewport.state.cursorMode)}` - const position = new THREE.Vector3((point.x + p0.x) / 2, (point.y + p0.y) / 2, (point.z + p0.z) / 2) - this.addOrUpdateTempLabel(label, position) - } - // tempLine 只需添加到场景一次 - if (!this.tempLine) { - this.viewport.scene.add(line) - this.tempLine = line - } - } - - // this.viewport.dispatchSignal('sceneGraphChanged') - } - - mouseup = (e: MouseEvent) => { - // 如果mouseMoved是true,那么它可能在移动,而不是点击 - if (!this.mouseMoved) { - // 右键点击表示完成绘图操作 - if (e.button === 2) { - this.viewport.state.cursorMode = 'normal' - - } else if (e.button === 0) { // 左键点击表示添加点 - this.onMouseClicked(e) - } - } - } - - onMouseClicked = (e: MouseEvent) => { - if (this.isCompleted) { - return - } - - const point = this.getClosestIntersection(e) - if (!point) { - return - } - - // 双击触发两次点击事件,我们需要避免这里的第二次点击 - const now = Date.now() - if (this.lastClickTime && (now - this.lastClickTime < 10)) return - - this.lastClickTime = now - - this.pointArray.push(point) - - const count = this.pointArray.length - const marker = this.createPointMarker(point) - marker.userData.point = point - marker.userData.pointIndex = count - 1 - this.group.add(marker) - // 把点加入拖拽控制器 - this.viewport.dragControl.setDragObjects([marker], 'push') - - if (this.polyline) { - this.polyline.geometry.setFromPoints(this.pointArray) - if (this.tempLabel && count > 1) { - const p0 = this.pointArray[count - 2] - this.tempLabel.position.set((p0.x + point.x) / 2, (p0.y + point.y) / 2, (p0.z + point.z) / 2) - this.group.add(this.tempLabel) - - // 创建距离测量线时,此处的 临时label 将作为正式的使用,不在this.clearTemp()中清除,故置为undefined - this.tempLabel = undefined - } - } - - // this.redrawComplete() - // this.viewport.dispatchSignal('sceneGraphChanged') - } - - /** - * Creates THREE.Line - */ - private createLine(): THREE.Line { - const geom = new THREE.BufferGeometry() - const obj = new THREE.Line(geom, RulerTool.LINE_MATERIAL) - obj.frustumCulled = false - obj.name = RulerTool.OBJ_NAME - obj.userData = { - type: 'line' - } - return obj - } - - /** - * 创建点标记 - */ - createPointMarker(position?: THREE.Vector3): THREE.Mesh { - const p = position - const scale = 0.25 - - const tt = new THREE.BoxGeometry(1, 1, 1) - const t2 = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 }) - const obj = new THREE.Mesh(tt, t2) - obj.scale.set(scale, 0.1, scale) - if (p) { - obj.position.set(p.x, p.y, p.z) - } - - obj.name = RulerTool.OBJ_NAME - - obj.userData = { - mode: this.viewport.state.cursorMode, - type: 'measure-marker' - } - return obj - } - - - /** - * 获取按下对应三维位置 - */ - getClosestIntersection(e: MouseEvent) { - const _point = new THREE.Vector2() - _point.x = e.offsetX / this.viewport.renderer.domElement.offsetWidth - _point.y = e.offsetY / this.viewport.renderer.domElement.offsetHeight - - const intersects = this.viewport.getIntersects(_point) - if (intersects && intersects.length > 2) { - if (intersects.length > 0 && intersects[0].distance < RulerTool.MAX_DISTANCE) { - return new Vector3( - intersects[0].point.x, - 0.1, - intersects[1].point.z - ) - } - } - return null - } - - /** - * 添加或更新临时标签和位置 - */ - addOrUpdateTempLabel(label: string, position: THREE.Vector3) { - if (!this.tempLabel) { - this.tempLabel = this.createLabel(label) - console.log('addOrUpdateTempLabel', label, position) - this.viewport.scene.add(this.tempLabel) - } - this.tempLabel.position.set(position.x, position.y, position.z) - this.tempLabel.element.innerHTML = label - } - - - /** - * 创建标签 - */ - createLabel(text: string): CSS2DObject { - const div = document.createElement('div') - div.className = 'css2dObjectLabel' - div.innerHTML = text - div.style.padding = '5px 8px' - div.style.color = '#fff' - div.style.fontSize = '14px' - div.style.position = 'absolute' - div.style.backgroundColor = 'rgba(25, 25, 25, 0.3)' - div.style.borderRadius = '12px' - div.style.top = '0px' - div.style.left = '0px' - // div.style.pointerEvents = 'none' //避免HTML元素影响场景的鼠标事件 - const obj = new CSS2DObject(div) - obj.name = RulerTool.LABEL_NAME - obj.userData = { - type: 'label' - } - return obj - } - - // 重绘完成 - redrawComplete() { - if (!this.tempPointMarker) return - - const point = this.tempPointMarker.userData.point - this.pointArray[this.tempPointMarker.userData.pointIndex] = point - const count = this.pointArray.length - - if (this.polyline) { - this.polyline.geometry.setFromPoints(this.pointArray) - // 如果是距离测量,则清除group中已有的label,再重新创建 - if (this.viewport.state.cursorMode === 'Ruler' && count > 1) { - this.clearCurrentLabel() - // 绘制label - for (let i = 0; i < count - 1; i++) { - const p0 = this.pointArray[i] - const p1 = this.pointArray[i + 1] - if (!p0 || !p1) continue - const dist = p0.distanceTo(p1) - const label = `${numberToString(dist)} ${getUnitString(this.viewport.state.cursorMode)}` - const position = new THREE.Vector3((p0.x + p1.x) / 2, (p0.y + p1.y) / 2, (p0.z + p1.z) / 2) - const labelObj = this.createLabel(label) - labelObj.position.set(position.x, position.y, position.z) - labelObj.element.innerHTML = label - this.group.add(labelObj) - } - } - } - - // this.destory() - } - - /** - * 清除当前group label - */ - clearCurrentLabel() { - for (let i = this.group.children.length - 1; i >= 0; i--) { - const c = this.group.children[i] - if (c.userData.type === 'label') { - this.group.remove(c) - } - } - } -} \ No newline at end of file diff --git a/src/types/Types.d.ts b/src/types/Types.d.ts index 017a840..98ab9dd 100644 --- a/src/types/Types.d.ts +++ b/src/types/Types.d.ts @@ -6,7 +6,15 @@ type CursorMode = | 'PointAdd' | 'LinkAdd' | 'LinkAdd2' - | 'Ruler' - | 'RulerArea' - | 'RulerAngle' + | 'Measure' + | 'MeasureArea' + | 'MeasureAngle' | 'selectByRec' + +type PointType = + 'measure-marker' + | 'pointMarker' + | 'pointMarker2' + | 'pointMarker3' + | 'pointMarker4' + | 'pointMarker5' \ No newline at end of file diff --git a/src/utils/webutils.ts b/src/utils/webutils.ts index 785a044..4762662 100644 --- a/src/utils/webutils.ts +++ b/src/utils/webutils.ts @@ -26,9 +26,9 @@ export function numberToString(num: number) { * 获取距离、面积或角度的单位字符串 */ export function getUnitString(mode: CursorMode): string { - if (mode === 'Ruler') return "m"; - if (mode === 'RulerArea') return "m²"; - if (mode === 'RulerAngle') return "°"; + if (mode === 'Measure') return "m"; + if (mode === 'MeasureArea') return "m²"; + if (mode === 'MeasureAngle') return "°"; return ""; }