You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

214 lines
6.5 KiB

import * as THREE from 'three'
import BaseRenderer from '@/core/base/BaseRenderer.ts'
import { getLineId } from '@/core/ModelUtils.ts'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
import { numberToString } from '@/utils/webutils.ts'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { Text } from 'troika-three-text'
import SimSunTTF from '@/assets/fonts/simsunb.ttf'
/**
* 辅助测量工具渲染器
*/
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
pointMaterial: THREE.Material
lineMaterial: LineMaterial
readonly defulePositionY = 0.01
readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.1, 0.1, 0.1)
readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0)
constructor(itemTypeName: string) {
super(itemTypeName)
}
async init() {
this.pointMaterial = new THREE.SpriteMaterial({
color: 0xFFFF99, // 0x303133,
transparent: true,
side: THREE.DoubleSide,
opacity: 1,
sizeAttenuation: true
})
this.lineMaterial = new LineMaterial({
color: 0xFF8C00,
linewidth: 1,
vertexColors: false,
dashed: false
})
return Promise.all([
super.init()
])
}
/**
* 所有的点,必须使用同一个尺寸, 改属性也无效
*/
override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, objects: THREE.Object3D[]) {
super.afterCreateOrUpdatePoint(item, option, objects)
const point = objects[0]
point.scale.set(this.defaultScale.x, this.defaultScale.y, this.defaultScale.z)
point.rotation.set(
THREE.MathUtils.degToRad(this.defaultRotation.x),
THREE.MathUtils.degToRad(this.defaultRotation.y),
THREE.MathUtils.degToRad(this.defaultRotation.z)
)
}
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.Sprite(this.pointMaterial as THREE.SpriteMaterial)
obj.name = MeasureRenderer.POINT_NAME
return [obj]
}
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, objects: THREE.Object3D[]) {
super.afterCreateOrUpdateLine(start, end, type, option, objects)
const startPoint = this.tempViewport?.entityManager.findObjectsById(start.id)?.[0]
const endPoint = this.tempViewport?.entityManager.findObjectsById(end.id)?.[0]
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 = objects[0].userData.labelObj
if (!labelObj || !labelObj.parent) {
labelObj = this.createLabel(label)
this.group.add(labelObj)
objects[0].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, objects: THREE.Object3D[]) {
super.afterDeleteLine(start, end, type, option, objects)
// 删除标签
const labelObj = objects[0]?.userData?.labelObj
if (labelObj && labelObj.parent) {
labelObj.parent.remove(labelObj)
}
}
/**
* 创建标签
*/
createLabel(text: string): Text | CSS2DObject {
if (this.useHtmlLabel) {
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 = MeasureRenderer.LABEL_NAME
return obj
} else {
const label = new Text()
label.text = text
label.font = SimSunTTF
label.fontSize = 0.4
label.color = '#333333'
label.opacity = 0.8
label.padding = 0.2
label.anchorX = 'center'
label.anchorY = 'middle'
label.depthOffset = 1
label.backgroundColor = '#000000' // 黑色背景
label.backgroundOpacity = 0.6 // 背景半透明
label.padding = 0.2 // 内边距
label.material.depthTest = false
label.name = MeasureRenderer.LABEL_NAME
label.sync()
return label
}
}
dispose() {
super.dispose()
if (this.group && this.group.parent) {
this.group.parent.remove(this.group)
}
this.group = undefined
this.pointMaterial.dispose()
this.lineMaterial.dispose()
}
}