123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- import { TextSprite } from "./TextSprite.js"
- import Vectors from "./Vectors.js"
- /**
- * 根据算法提供的全景图点数据,在3d中标记并围成一个矩形线框
- */
- export default class PanoWireframe extends THREE.Group {
- constructor(scene, sceneCode) {
- super()
- this.sceneCode = sceneCode
- scene.add(this)
- }
- clearAll() {
- // 清除已有线框
- this.traverse(obj => {
- if (obj.isMesh) {
- obj.geometry.dispose()
- obj.material.dispose()
- }
- })
- this.clear()
- }
- /**
- * 加载点位标记数据
- * @param {*} panoId
- */
- async load(panoId) {
- let data = await axios.post(
- "/service/scene/sceneMarkShape/getInfo",
- {
- num: this.sceneCode,
- imagePath: panoId + ".jpg"
- }
- )
- data = data.data
- if (!data.data || !data.success) return
- let { shapes, imageHeight, imageWidth } = data.data
- let labels = ""
- shapes.forEach(shape => {
- // 填充色和线框色
- // let { fill_color, line_color } = shape
- let { fill_color, color } = shape
- let line_color = [...color, 255]
- if (!fill_color) fill_color = [255, 255, 255, 0]
- if (!line_color) line_color = [255, 0, 0, 255]
- this.showSignalFrom2d(shape.category, shape.bbox, imageWidth, imageHeight, {
- fill: {
- color: new THREE.Color().setRGB(fill_color[0] / 255, fill_color[1] / 255, fill_color[2] / 255),
- opacity: fill_color[3] / 255,
- },
- line: {
- color: new THREE.Color().setRGB(line_color[0] / 255, line_color[1] / 255, line_color[2] / 255),
- opacity: line_color[3] / 255,
- },
- })
- labels += "<li>" + shape.label + '</li>'
- })
- return labels
- }
- /**
- * 根据坐标标记全景图
- */
- showSignalFrom2d(name, rect, w, h, options) {
- // 目前rect给的是矩形对角的两个点坐标,将它扩展成四个顶点
- let cornerArr = [
- [rect[0], rect[1]],
- [rect[2], rect[1]],
- [rect[2], rect[3]],
- [rect[0], rect[3]],
- ]
- // 2d坐标转3d坐标
- let transform2dTo3d = point => {
- // 计算方向向量
- let x = point[0],
- y = point[1]
- let yaw = (-x / w) * (Math.PI * 2)
- let pitch = Math.PI / 2 - (y / h) * Math.PI
- let dir = new THREE.Vector3()
- dir.copy(Vectors.RIGHT).applyAxisAngle(Vectors.BACK, pitch).applyAxisAngle(Vectors.UP, yaw)
- return dir
- }
- // // 计算矩形线框中点坐标(取x、y的平均值)
- // let center = [cornerArr.reduce((a, b) => [a[0] + b[0], 0])[0] / cornerArr.length, cornerArr.reduce((a, b) => [0, a[1] + b[1]])[1] / cornerArr.length]
- // let centerVec = transform2dTo3d(center) // 计算中点3d坐标
- // 根据四个顶点,填充中间点
- let pointArr = []
- for (let i = 0; i < cornerArr.length; i++) {
- let corner1 = cornerArr[i]
- let corner2 = cornerArr[(i + 1) % cornerArr.length]
- pointArr.push(corner1)
- // 横向角度超过150度时,3d中边框的弧线已经不太明显,准确画出全景图线框
- if ((rect[2] - rect[0]) / w < 5 / 12 && i % 2 == 0) continue
- const vec = [corner2[0] - corner1[0], corner2[1] - corner1[1]]
- let length = Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1])
- let num = length / 150
- for (let j = 1; j <= num; j++) {
- pointArr.push([corner1[0] + (vec[0] / num) * j, corner1[1] + (vec[1] / num) * j])
- }
- }
- let points = []
- pointArr.forEach(point => {
- let dir = transform2dTo3d(point)
- // points.push(dir.sub(centerVec)) // 计算其他点相对于中点的坐标,方便旋转平移等
- points.push(dir)
- })
- // 线框
- const lineGeometry = new THREE.BufferGeometry().setFromPoints(points)
- const lineMaterial = new THREE.LineBasicMaterial({ color: options.line.color, opacity: options.line.opacity, transparent: true, depthTest: false })
- const wireframe = new THREE.LineLoop(lineGeometry, lineMaterial)
- // wireframe.position.copy(centerVec) // 将中点作为线框坐标
- wireframe.renderOrder = 100
- // 填充颜色
- const fillGeometry = lineGeometry.clone().setIndex(new THREE.BufferAttribute(new Uint16Array([0, 1, 3, 2, 3, 1]), 1))
- const fillMaterial = new THREE.MeshBasicMaterial({ color: options.fill.color, opacity: options.fill.opacity, transparent: true, side: THREE.DoubleSide, depthTest: false })
- const plane = new THREE.Mesh(fillGeometry, fillMaterial)
- plane.renderOrder = wireframe.renderOrder - 1
- wireframe.add(plane)
- // 名称
- const textMesh = new TextSprite({
- text: name,
- backgroundColor: { r: options.line.color.r * 255, g: options.line.color.g * 255, b: options.line.color.b * 255, a: 0.4 },
- textColor: { r: 255, g: 255, b: 255, a: 1 },
- borderRadius: 15,
- renderOrder: wireframe.renderOrder + 1
- })
- textMesh.position.copy(points[0]) // 线框左上角
- textMesh.lookAt(0, 0, 0) // 看向相机
- textMesh.scale.set(0.2, 0.2, 0.2)
- let group = new THREE.Group()
- group.add(wireframe)
- group.add(textMesh)
- this.add(group)
- }
- }
|