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
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()
|
|
}
|
|
}
|