Browse Source

measure createLabel

master
修宁 7 months ago
parent
commit
8c2f22f539
  1. 8
      src/designer/Viewport.ts
  2. 30
      src/designer/model2DEditor/EsDragControls.ts
  3. 183
      src/designer/model2DEditor/tools/MeasureTool.ts
  4. 35
      src/model/ModelUtils.ts
  5. 10
      src/model/example1.js
  6. 24
      src/model/itemType/ItemTypeBase.ts
  7. 215
      src/model/itemType/ItemTypeLineBase.ts
  8. 81
      src/model/itemType/measure/Measure.ts

8
src/designer/Viewport.ts

@ -23,6 +23,8 @@ import MeasureTool from '@/designer/model2DEditor/tools/MeasureTool.ts'
import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer' import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer'
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
import type { ICursorTool } from '@/designer/model2DEditor/tools/CursorTool.ts' import type { ICursorTool } from '@/designer/model2DEditor/tools/CursorTool.ts'
import { getAllItemTypes } from '@/runtime/DefineItemType.ts'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
/** /**
* *
@ -81,7 +83,7 @@ export default class Viewport {
} }
dispatchSignal(signal: string, data?: any) { dispatchSignal(signal: string, data?: any) {
console.log('signal', signal, data) // console.log('signal', signal, data)
} }
/** /**
@ -246,6 +248,10 @@ export default class Viewport {
this.measure = new MeasureTool() this.measure = new MeasureTool()
this.measure.init(this) this.measure.init(this)
_.forEach(getAllItemTypes(), (itemType: ItemTypeDefineOption) => {
itemType.clazz.afterAddViewport(this)
})
this.state.isReady = true this.state.isReady = true
} }

30
src/designer/model2DEditor/EsDragControls.ts

@ -2,6 +2,8 @@ import { DragControls } from './DragControls.js'
import * as THREE from 'three' import * as THREE from 'three'
import type Viewport from '@/designer/Viewport.ts' import type Viewport from '@/designer/Viewport.ts'
import Constract from '@/designer/Constract.ts' import Constract from '@/designer/Constract.ts'
import { getItemTypeByName } from '@/runtime/DefineItemType.ts'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
// dragControls 绑定函数 // dragControls 绑定函数
let dragStartFn, dragFn, dragEndFn, clickblankFn let dragStartFn, dragFn, dragEndFn, clickblankFn
@ -77,11 +79,17 @@ export default class EsDragControls {
// 记录拖拽按下的位置和对象 // 记录拖拽按下的位置和对象
this.onDownPosition = { x: e.e.clientX, y: e.e.clientY } this.onDownPosition = { x: e.e.clientX, y: e.e.clientY }
switch (e.object.userData.type) { if (e.object.userData?.type) {
case Constract.MeasureMarker: const itemType: ItemTypeDefineOption = getItemTypeByName(e.object.userData.type)
this.viewport.measure.dragPointStart(e.object) if (itemType?.clazz) {
break itemType.clazz.dragPointStart(this.viewport, e.object, e.e)
}
} }
// switch (e.object.userData.type) {
// case Constract.MeasureMarker:
// this.viewport.measure.dragPointStart(e.object)
// break
// }
} }
// 拖拽中 // 拖拽中
@ -108,11 +116,17 @@ export default class EsDragControls {
} }
} }
switch (e.object.userData.type) { if (e.object.userData?.type) {
case Constract.MeasureMarker: const itemType: ItemTypeDefineOption = getItemTypeByName(e.object.userData.type)
this.viewport.measure.dragPointComplete() if (itemType?.clazz) {
break itemType.clazz.dragPointComplete(this.viewport)
}
} }
// switch (e.object.userData.type) {
// case Constract.MeasureMarker:
// this.viewport.measure.dragPointComplete()
// break
// }
} }
// 点击可拖拽物体之外 // 点击可拖拽物体之外

183
src/designer/model2DEditor/tools/MeasureTool.ts

@ -4,6 +4,9 @@ import { getUnitString, numberToString } from '@/utils/webutils'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import Constract from '@/designer/Constract.ts' import Constract from '@/designer/Constract.ts'
import type { ICursorTool } from '@/designer/model2DEditor/tools/CursorTool.ts' import type { ICursorTool } from '@/designer/model2DEditor/tools/CursorTool.ts'
import type Measure from '@/model/itemType/measure/Measure.ts'
import { getItemTypeByName } from '@/runtime/DefineItemType.ts'
import { LABEL_NAME, LINE_NAME } from '@/model/itemType/measure/Measure.ts'
let pdFn, pmFn, puFn, kdFn let pdFn, pmFn, puFn, kdFn
@ -11,11 +14,6 @@ let pdFn, pmFn, puFn, kdFn
* threejs object_for_measure * threejs object_for_measure
*/ */
export default class MeasureTool implements ICursorTool { export default class MeasureTool implements ICursorTool {
static OBJ_NAME = 'measure_'
static LABEL_NAME = 'measure_label'
static LINE_NAME = 'measure_line'
static MAX_DISTANCE = 500 //当相交物体的距离太远时,忽略它
/** /**
* Three.js . * Three.js .
* : * :
@ -32,7 +30,8 @@ export default class MeasureTool implements ICursorTool {
/** /**
* , 线. 线 * , 线. 线
*/ */
group: THREE.Group measure: Measure
/** /**
* *
*/ */
@ -88,6 +87,11 @@ export default class MeasureTool implements ICursorTool {
mode: CursorMode mode: CursorMode
private draggingIndex: number = -1
private connectedLines: THREE.Line[] = []
private connectedLabels: CSS2DObject[] = []
private originalPosition: THREE.Vector3 = new THREE.Vector3()
/** /**
* *
*/ */
@ -95,12 +99,7 @@ export default class MeasureTool implements ICursorTool {
this.viewport = viewport this.viewport = viewport
this.canvas = this.viewport.renderer.domElement as HTMLCanvasElement this.canvas = this.viewport.renderer.domElement as HTMLCanvasElement
this.group = new THREE.Group() this.measure = getItemTypeByName('measure')!.clazz
this.group.name = `${MeasureTool.OBJ_NAME}_group`
this.group.userData = {
mode: Constract.CursorModeMeasure
}
this.viewport.scene.add(this.group)
} }
/** /**
@ -204,12 +203,10 @@ export default class MeasureTool implements ICursorTool {
geom.setFromPoints([lastPoint, point]) geom.setFromPoints([lastPoint, point])
} }
if (this.mode === Constract.CursorModeMeasure) { const dist = p0.distanceTo(point)
const dist = p0.distanceTo(point) const label = `${numberToString(dist)} ${getUnitString(this.mode)}`
const label = `${numberToString(dist)} ${getUnitString(this.mode)}` const position = new THREE.Vector3((point.x + p0.x) / 2, (point.y + p0.y) / 2, (point.z + p0.z) / 2)
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.addOrUpdateTempLabel(label, position)
}
} }
// this.viewport.dispatchSignal('sceneGraphChanged') // this.viewport.dispatchSignal('sceneGraphChanged')
@ -259,7 +256,7 @@ export default class MeasureTool implements ICursorTool {
const marker = this.createPointMarker(point) const marker = this.createPointMarker(point)
marker.userData.point = point marker.userData.point = point
marker.userData.pointIndex = count - 1 marker.userData.pointIndex = count - 1
this.group.add(marker) this.measure.group.add(marker)
// 把点加入拖拽控制器 // 把点加入拖拽控制器
this.viewport.dragControl.setDragObjects([marker], 'push') this.viewport.dragControl.setDragObjects([marker], 'push')
@ -270,13 +267,13 @@ export default class MeasureTool implements ICursorTool {
this.tempLabel.position.set((p0.x + point.x) / 2, (p0.y + point.y) / 2, (p0.z + point.z) / 2) this.tempLabel.position.set((p0.x + point.x) / 2, (p0.y + point.y) / 2, (p0.z + point.z) / 2)
this.tempLabel.userData.pointIndices = [count - 2, count - 1] this.tempLabel.userData.pointIndices = [count - 2, count - 1]
this.group.add(this.tempLabel) this.measure.group.add(this.tempLabel)
this.tempLabel = undefined this.tempLabel = undefined
// 临时线 // 临时线
this.tempLine.geometry.setFromPoints([p0, point]) this.tempLine.geometry.setFromPoints([p0, point])
this.tempLine.userData.pointIndices = [count - 2, count - 1] this.tempLine.userData.pointIndices = [count - 2, count - 1]
this.group.add(this.tempLine) this.measure.group.add(this.tempLine)
this.tempLine = undefined this.tempLine = undefined
} }
@ -327,154 +324,12 @@ export default class MeasureTool implements ICursorTool {
*/ */
addOrUpdateTempLabel(label: string, position: THREE.Vector3) { addOrUpdateTempLabel(label: string, position: THREE.Vector3) {
if (!this.tempLabel) { if (!this.tempLabel) {
this.tempLabel = this.createLabel(label) this.tempLabel = this.measure.createLabel(label)
console.log('addOrUpdateTempLabel', label, position)
this.viewport.scene.add(this.tempLabel) this.viewport.scene.add(this.tempLabel)
} }
this.tempLabel.position.set(position.x, position.y, position.z) this.tempLabel.position.set(position.x, position.y, position.z)
this.tempLabel.element.innerHTML = label 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
}
private draggingIndex: number = -1
private connectedLines: THREE.Line[] = []
private connectedLabels: CSS2DObject[] = []
private originalPosition: THREE.Vector3 = new THREE.Vector3()
/**
* DragControls
* description:
* 线
* 线
* move方法线
* @param point .
*/
dragPointStart(point: THREE.Mesh) {
this.group = point.parent as THREE.Group
this.isCompleted = false
// 获取当前拖拽点的索引
this.draggingIndex = point.userData.pointIndex
this.originalPosition.copy(point.position)
// 收集相关联的线段和标签
this.connectedLines = []
this.connectedLabels = []
// 遍历group查找相关线段和标签
this.group.children.forEach(child => {
// 处理线段
if (child.name === MeasureTool.LINE_NAME && child.userData.pointIndices) {
const [startIdx, endIdx] = child.userData.pointIndices
if (startIdx === this.draggingIndex || endIdx === this.draggingIndex) {
this.connectedLines.push(child as THREE.Line)
}
}
// 处理标签
if (child.name === MeasureTool.LABEL_NAME && child.userData.pointIndices) {
const [startIdx, endIdx] = child.userData.pointIndices
if (startIdx === this.draggingIndex || endIdx === this.draggingIndex) {
this.connectedLabels.push(child as CSS2DObject)
}
}
})
// 监听move事件
pmFn = this.redrawMousemove.bind(this)
this.canvas.addEventListener('pointermove', pmFn)
console.log('connectedLines:', this.connectedLines)
console.log('connectedLabels:', this.connectedLabels)
// 这个方法会在拖拽开始时被调用,记录当前点的索引和位置,
// 需要找到与这个点相关的所有测量点、线、标签,并将它们添加到临时数组中。
// 并创建临时线条用于连接前后点。
}
/**
*
*/
redrawMousemove(e: MouseEvent) {
if (this.draggingIndex === -1) return
let newPosition = this.viewport.getClosestIntersection(e)
// 更新点数组中的坐标
this.pointArray[this.draggingIndex].copy(newPosition)
// 更新所有相关线段
this.connectedLines.forEach(line => {
const [startIdx, endIdx] = line.userData.pointIndices
const startPoint = this.pointArray[startIdx]
const endPoint = this.pointArray[endIdx]
// 更新线段几何体
const geometry = line.geometry as THREE.BufferGeometry
geometry.setFromPoints([startPoint, endPoint])
geometry.attributes.position.needsUpdate = true
})
// 更新所有相关标签
this.connectedLabels.forEach(label => {
const [startIdx, endIdx] = label.userData.pointIndices
const startPoint = this.pointArray[startIdx]
const endPoint = this.pointArray[endIdx]
// 计算中间位置和距离
const midPoint = new THREE.Vector3()
.addVectors(startPoint, endPoint)
.multiplyScalar(0.5)
const distance = startPoint.distanceTo(endPoint)
// 更新标签位置和内容
label.position.copy(midPoint)
label.element.innerHTML = `${numberToString(distance)} ${getUnitString(this.mode)}`
})
// 更新点的可视化位置
const pointObj = this.group.children.find(child =>
child.userData.pointIndex === this.draggingIndex
) as THREE.Mesh
if (pointObj) {
pointObj.position.copy(newPosition)
}
// 在拖拽过程中,监听鼠标move方法
// 更新临时点的位置,并根据当前点的索引更新临时线条的几何体、重新计算长度、更新标签位置等
}
/**
* DragControls
*/
dragPointComplete() {
// 将临时点、线、标签添加到 group 中, 变成正式的测量
// 删除鼠标事件监听
this.canvas.removeEventListener('pointermove', pmFn)
pmFn = undefined
}
} }

35
src/model/ModelUtils.ts

@ -2,6 +2,41 @@ import * as THREE from 'three'
import type { ItemJson, ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts' import type { ItemJson, ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import { getAllItemTypes, getItemTypeByName } from '@/runtime/DefineItemType.ts' import { getAllItemTypes, getItemTypeByName } from '@/runtime/DefineItemType.ts'
/**
* uuid Object3D
*/
export function findObject3DById(scene: THREE.Object3D, uuid: string): THREE.Object3D | undefined {
const rets = findObject3DByCondition(scene, object => object.uuid === uuid)
if (rets.length > 0) {
return rets[0]
}
return undefined
}
/**
* Object3D
*/
export function findObject3DByCondition(scene: THREE.Object3D, condition: (object: THREE.Object3D) => boolean): THREE.Object3D[] {
const foundObjects: THREE.Object3D[] = []
// 定义一个内部递归函数来遍历每个节点及其子节点
function traverse(obj: THREE.Object3D) {
if (condition(obj)) {
foundObjects.push(obj)
}
// 遍历当前对象的所有子对象
for (let i = 0; i < obj.children.length; i++) {
traverse(obj.children[i])
}
}
// 开始从场景根节点进行遍历
traverse(scene)
return foundObjects
}
export function loadSceneFromJson(scene: THREE.Scene, items: ItemJson[]) { export function loadSceneFromJson(scene: THREE.Scene, items: ItemJson[]) {
console.time('loadSceneFromJson') console.time('loadSceneFromJson')
const object3ds = loadObject3DFromJson(items) const object3ds = loadObject3DFromJson(items)

10
src/model/example1.js

@ -43,7 +43,7 @@ export default {
t: 'measure', a: 'ln', l: '测量2', c: '#ff0000', t: 'measure', a: 'ln', l: '测量2', c: '#ff0000',
tf: [[-9.0, 0, 3], [0, 0, 0], [0.25, 0.1, 0.25]], tf: [[-9.0, 0, 3], [0, 0, 0], [0.25, 0.1, 0.25]],
dt: { dt: {
link: ['p3'] link: ['p3', 'p4']
} }
}, },
{ {
@ -53,6 +53,14 @@ export default {
dt: { dt: {
link: [] link: []
} }
},
{
id: 'p4',
t: 'measure', a: 'ln', l: '测量3', c: '#ff0000',
tf: [[-9.0, 0, 8], [0, 0, 0], [0.25, 0.1, 0.25]],
dt: {
link: []
}
} }
] ]
} }

24
src/model/itemType/ItemTypeBase.ts

@ -2,8 +2,15 @@ import { type Object3D } from 'three'
import type WorldModel from '@/model/WorldModel.ts' import type WorldModel from '@/model/WorldModel.ts'
import type { ItemJson, ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts' import type { ItemJson, ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import * as THREE from 'three' import * as THREE from 'three'
import type Viewport from '@/designer/Viewport.ts'
export default abstract class ItemTypeBase { export default abstract class ItemTypeBase {
/**
*
*/
pointArray: THREE.Object3D[] = []
name: string name: string
option: ItemTypeDefineOption option: ItemTypeDefineOption
worldModel: WorldModel worldModel: WorldModel
@ -26,4 +33,21 @@ export default abstract class ItemTypeBase {
*/ */
afterAddScene(scene: THREE.Scene, objects: THREE.Object3D[]): void { afterAddScene(scene: THREE.Scene, objects: THREE.Object3D[]): void {
} }
/**
* viewport
*/
afterAddViewport(viewport: Viewport): void {
viewport.dragControl.setDragObjects(this.pointArray, 'push')
}
/**
* EsDragControls
*/
abstract dragPointStart(viewport: Viewport, point: THREE.Mesh)
/**
* EsDragControls
*/
abstract dragPointComplete(viewport: Viewport)
} }

215
src/model/itemType/ItemTypeLineBase.ts

@ -2,40 +2,79 @@ import * as THREE from 'three'
import ItemTypeBase from '@/model/itemType/ItemTypeBase.ts' import ItemTypeBase from '@/model/itemType/ItemTypeBase.ts'
import type { ItemJson } from '@/model/itemType/ItemTypeDefine.ts' import type { ItemJson } from '@/model/itemType/ItemTypeDefine.ts'
import type WorldModel from '@/model/WorldModel.ts' import type WorldModel from '@/model/WorldModel.ts'
import type Viewport from '@/designer/Viewport.ts'
import { findObject3DByCondition, findObject3DById } from '@/model/ModelUtils.ts'
let pmFn
/** /**
* ILineType 线 * ILineType 线
* 线 * 线, ,
*/ */
export default abstract class ItemTypeLineBase extends ItemTypeBase { export default abstract class ItemTypeLineBase extends ItemTypeBase {
private relationPoints: THREE.Mesh[] = []
private dragViewport: Viewport | undefined
private dragPoint: THREE.Mesh | undefined
/** abstract createPointBasic(position: THREE.Vector3): THREE.Object3D
*
*/
pointArray: THREE.Object3D[] = []
/** abstract createLineBasic(): THREE.Line
* 线
*/
linkArray: THREE.Object3D[] = []
public init(worldModel: WorldModel) { public init(worldModel: WorldModel) {
return super.init(worldModel).then(() => { return super.init(worldModel).then(() => {
// 初始化方法,子类可以重写
this.pointArray.length = 0
this.linkArray.length = 0
}) })
} }
afterLoadGroup(group: THREE.Group): void { afterLoadGroup(group: THREE.Group): void {
} }
afterLoadLine(line: THREE.Line): void { afterCreateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D): void {
} }
afterLoadPoint(point: THREE.Object3D): void { afterLoadPoint(point: THREE.Object3D): void {
} }
afterUpdatePoint(point: THREE.Object3D, updatedLines: THREE.Object3D[]): void {
}
afterUpdateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D): void {
}
createLine(scene: THREE.Scene, startPoint: THREE.Object3D, endPoint: THREE.Object3D): THREE.Line {
const line = this.createLineBasic()
const geom = line.geometry
geom.setFromPoints([startPoint.position, endPoint.position])
if (!line.userData) {
line.userData = {}
}
line.userData.lineStartId = startPoint.uuid
line.userData.lineEndId = endPoint.uuid
if (startPoint.parent) {
startPoint.parent.add(line)
} else {
scene.add(line)
}
if (!startPoint.userData.lines) {
startPoint.userData.lines = []
}
startPoint.userData.lines.push(line.uuid)
if (!endPoint.userData.lines) {
endPoint.userData.lines = []
}
endPoint.userData.lines.push(line.uuid)
this.afterCreateLine(line, startPoint, endPoint)
return line
}
/**
* link 线
*/
afterAddScene(scene: THREE.Scene, objects: THREE.Object3D[]) { afterAddScene(scene: THREE.Scene, objects: THREE.Object3D[]) {
super.afterAddScene(scene, objects) super.afterAddScene(scene, objects)
@ -49,28 +88,43 @@ export default abstract class ItemTypeLineBase extends ItemTypeBase {
for (let j = 0; j < linkArray.length; j++) { for (let j = 0; j < linkArray.length; j++) {
const linkId = linkArray[j] const linkId = linkArray[j]
// 在 pointArray 中查找对应的点 // 在 pointArray 中查找对应的点
const endPoint = this.pointArray.find(p => p.uuid === linkId) const endPoint = findObject3DById(scene, linkId)
if (!endPoint) { if (!endPoint) {
console.warn('not found link point uuid=${}', linkId) console.warn('not found link point uuid=${}', linkId)
continue continue
} }
const line = this.createLine() const line = this.createLine(scene, startPoint, endPoint)
const geom = line.geometry
geom.setFromPoints([startPoint.position, endPoint.position])
if (startPoint.parent) {
startPoint.parent.add(line)
} else {
scene.add(line)
}
this.afterLoadLine(line)
this.linkArray.push(line)
} }
} }
} }
createPoint(position: THREE.Vector3, item: ItemJson): THREE.Object3D {
const point = this.createPointBasic(position)
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
point.rotation.set(
THREE.MathUtils.degToRad(item.tf[1][0]),
THREE.MathUtils.degToRad(item.tf[1][1]),
THREE.MathUtils.degToRad(item.tf[1][2])
)
point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2])
this.pointArray.push(point)
this.afterLoadPoint(point)
return point
}
/**
* Json item
*/
override loadFromJson(item: ItemJson): undefined | THREE.Object3D { override loadFromJson(item: ItemJson): undefined | THREE.Object3D {
if (item.a === 'gp') { if (item.a === 'gp') {
// gp 是为了分组而存在的 // gp 是为了分组而存在的
@ -95,32 +149,97 @@ export default abstract class ItemTypeLineBase extends ItemTypeBase {
item.tf[0][2] item.tf[0][2]
) )
const point = this.createPoint(position) return this.createPoint(position, item)
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
point.rotation.set(
THREE.MathUtils.degToRad(item.tf[1][0]),
THREE.MathUtils.degToRad(item.tf[1][1]),
THREE.MathUtils.degToRad(item.tf[1][2])
)
point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2])
this.pointArray.push(point)
this.afterLoadPoint(point)
return point
} }
console.error('ItemTypeLineBase.loadFromJson: Unsupported', item) console.error('ItemTypeLineBase.loadFromJson: Unsupported', item)
} }
abstract createPoint(position: THREE.Vector3): THREE.Object3D
abstract createLine(): THREE.Line /**
* DragControls
*/
dragPointStart(viewport: Viewport, point: THREE.Mesh) {
console.log('dragPoint:', point)
this.dragViewport = viewport
this.dragPoint = point
const canvas = viewport.renderer.domElement
// 收集相关联的线段和标签
const relationPoints = new Set<THREE.Object3D>()
// 找到与 point 有关联的节点, 无论是 a->b 还是 b->a 都需要收集
findObject3DByCondition(viewport.scene, (targetObj: THREE.Object3D) => {
if (targetObj.uuid === point.uuid) {
return false
}
// 无论 a->b 还是 b->a 都需要收集
if (_.includes(targetObj.userData?.link, point.uuid) || _.includes(point.userData?.link, targetObj.uuid)) {
relationPoints.add(targetObj)
}
})
//@ts-ignore
this.relationPoints = Array.from(relationPoints)
// 监听move事件
pmFn = this.redrawMousemove.bind(this)
canvas.addEventListener('pointermove', pmFn)
}
/**
* DragControls
*/
redrawMousemove(e: MouseEvent) {
if (!this.dragViewport || !this.dragPoint) return
const updateLines: THREE.Object3D[] = []
// 更新所有相关线段
_.forEach(this.relationPoints, (targetPoint, idx) => {
if (targetPoint.uuid === this.dragPoint.uuid) {
return
}
// 判断谁是起点,谁是终点
let startPoint: THREE.Object3D
let endPoint: THREE.Object3D
if (_.includes(targetPoint.userData?.link, this.dragPoint.uuid)) {
startPoint = targetPoint
endPoint = this.dragPoint
} else {
startPoint = this.dragPoint
endPoint = targetPoint
}
// 找到 startPoint 与 this.dragPoint 之间的线段
let line = findObject3DByCondition(this.dragViewport.scene, (obj) => {
return obj.userData.lineStartId === startPoint.uuid && obj.userData.lineEndId === endPoint.uuid
})[0] as THREE.Line
if (!line) {
line = this.createLine(this.dragViewport.scene, startPoint, endPoint)
}
// 更新线段几何体
const geometry = line.geometry as THREE.BufferGeometry
geometry.setFromPoints([startPoint.position, endPoint.position])
geometry.attributes.position.needsUpdate = true
this.afterUpdateLine(line, startPoint, endPoint)
updateLines.push(line)
})
this.afterUpdatePoint(this.dragPoint, updateLines)
}
/**
* DragControls
*/
dragPointComplete(viewport: Viewport) {
// 删除鼠标事件监听
viewport.renderer.domElement.removeEventListener('pointermove', pmFn)
pmFn = undefined
}
} }

81
src/model/itemType/measure/Measure.ts

@ -2,6 +2,10 @@ import * as THREE from 'three'
import ItemTypeLineBase from '@/model/itemType/ItemTypeLineBase.ts' import ItemTypeLineBase from '@/model/itemType/ItemTypeLineBase.ts'
import { Material, Object3D, Scene } from 'three' import { Material, Object3D, Scene } from 'three'
import WorldModel from '@/model/WorldModel.ts' import WorldModel from '@/model/WorldModel.ts'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import Constract from '@/designer/Constract.ts'
import { numberToString } from '@/utils/webutils.ts'
import { findObject3DById } from '@/model/ModelUtils.ts'
export const GROUP_NAME = 'measure-group' export const GROUP_NAME = 'measure-group'
export const POINT_NAME = 'measure_point' export const POINT_NAME = 'measure_point'
@ -39,11 +43,6 @@ export default class Measure extends ItemTypeLineBase {
return Promise.resolve() return Promise.resolve()
} }
// 创建完线之后,创建 label
afterLoadLine(line: THREE.Line) {
super.afterLoadLine(line)
}
afterLoadGroup(group: THREE.Group) { afterLoadGroup(group: THREE.Group) {
super.afterLoadGroup(group) super.afterLoadGroup(group)
@ -67,7 +66,7 @@ export default class Measure extends ItemTypeLineBase {
/** /**
* *
*/ */
createPoint(position?: THREE.Vector3): THREE.Object3D { createPointBasic(position?: THREE.Vector3): THREE.Object3D {
const p = position const p = position
const scale = 0.25 const scale = 0.25
@ -84,7 +83,7 @@ export default class Measure extends ItemTypeLineBase {
/** /**
* 线 * 线
*/ */
createLine(): THREE.Line { createLineBasic(): THREE.Line {
const geom = new THREE.BufferGeometry() const geom = new THREE.BufferGeometry()
const obj = new THREE.Line(geom, this.lineMaterial) const obj = new THREE.Line(geom, this.lineMaterial)
obj.frustumCulled = false obj.frustumCulled = false
@ -92,4 +91,72 @@ export default class Measure extends ItemTypeLineBase {
obj.uuid = THREE.MathUtils.generateUUID() obj.uuid = THREE.MathUtils.generateUUID()
return obj return obj
} }
// 创建完线之后,创建 label
afterCreateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
super.afterCreateLine(line, startPoint, endPoint)
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)
const labelObj = this.createLabel(label)
labelObj.position.set(position.x, 0.1, position.z)
labelObj.element.innerHTML = label
line.userData.labelId = labelObj.uuid
this.group.add(labelObj)
}
afterUpdateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
super.afterUpdateLine(line, startPoint, endPoint)
const p0 = startPoint.position
const p1 = endPoint.position
const dist = p0.distanceTo(p1)
const label = `${numberToString(dist)}`
const position = new THREE.Vector3().addVectors(p0, p1).multiplyScalar(0.5)
const labelObj: CSS2DObject = findObject3DById(this.group, line.userData.labelId) as CSS2DObject
if (labelObj) {
labelObj.position.set(position.x, 0.1, position.z)
labelObj.element.innerHTML = label
} else {
// 如果没有找到,则创建新的标签
const newLabelObj = this.createLabel(label)
newLabelObj.position.set(position.x, position.y, position.z)
newLabelObj.element.innerHTML = label
line.userData.labelId = labelObj.uuid
this.group.add(newLabelObj)
}
}
/**
*
*/
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 = LABEL_NAME
obj.userData = {
type: Constract.MeasureLabel
}
return obj
}
} }
Loading…
Cancel
Save