import type Viewport from '@/core/engine/Viewport' import * as THREE from 'three' import { getLineId } from '@/core/ModelUtils.ts' import { Line2 } from 'three/examples/jsm/lines/Line2' /** * 基本渲染器基类 * 定义了点 / 线如何渲染到 Three.js 场景中 */ export default abstract class BaseRenderer { /** * 每次 beginUpdate 时记录临时 viewport, endUpdate 之后要马上删除, 因为 BaseRenderer 全局只有一个实例, 而 viewport 有多个 */ tempViewport?: Viewport = undefined /** * 开始更新 * @param viewport 当前视口 */ beginUpdate(viewport: Viewport): void { this.tempViewport = viewport } /** * 创建一个最基本的点对象, 不用管 item 的 name / id / 位置 / 转换 / 大小 和 userData, 除非有明确定义 */ abstract createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] /** * 创建测量线 */ abstract createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D[] /** * 创建一个点 * @param item 点的定义 * @param option 渲染选项 */ createPoint(item: ItemJson, option?: RendererCudOption) { // 由基础类创造一个属于自己的点演示 const points = this.createPointBasic(item, option) _.forEach(points, (point) => { if (item.name) { point.name = item.name } point.userData = _.cloneDeep(item.dt) || {} _.extend(point.userData, { t: item.t, center: [], in: [], out: [], selectable: true, protected: false }) point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) 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.tempViewport.entityManager.appendObject(item.id, points) } /** * 删除一个点 * @param id 点的唯一标识 * @param option 渲染选项 */ deletePoint(id: string, option?: RendererCudOption) { const objects = this.tempViewport.entityManager.findObjectsById(id) if (objects) { this.tempViewport.scene.remove(...objects) } this.tempViewport.entityManager.deleteEntityOnly(id) this.tempViewport.entityManager.deleteObjectsOnly(id) } /** * 更新一个点 * @param item 点的定义 * @param option 渲染选项 */ updatePoint(item: ItemJson, option?: RendererCudOption) { const objects = this.tempViewport.entityManager.findObjectsById(item.id) if (!objects || objects.length === 0) { console.warn(`Point with ID "${item.id}" does not exist.`) return } _.forEach(objects, (point) => { point.name = item.name || point.name point.userData = _.cloneDeep(item.dt) || {} _.extend(point.userData, { t: item.t, center: [], in: [], out: [], selectable: true, protected: false }) 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]) }) } /** * 创建一根线 * @param start 起点 * @param end 终点 * @param type 线的类型 * @param option 渲染选项 */ createLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { const lines = this.createLineBasic(start, end, type) const id = getLineId(start.id, end.id, type) const startPoint = this.tempViewport.entityManager.findObjectsById(start.id)?.[0] const endPoint = this.tempViewport.entityManager.findObjectsById(end.id)?.[0] _.forEach(lines, (line) => { line.userData = { t: 'line', start: start.id, end: end.id, type: type } line.name = `${start.id}-${end.id}-${type}` this.tempViewport.entityManager.findObjectsById(id) const geom = line.geometry geom.setFromPoints([startPoint.position, endPoint.position]) }) this.tempViewport.entityManager.appendLineObject(id, lines) } /** * 更新一根线 * @param start 起点 * @param end 终点 * @param type 线的类型 * @param option 渲染选项 */ updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { const lineId = getLineId(start.id, end.id, type) const lines = this.tempViewport.entityManager.findLineObjectsById(lineId) _.forEach(lines, (line: THREE.Object3D) => { const startPoint = this.tempViewport.entityManager.findObjectsById(start.id)?.[0] const endPoint = this.tempViewport.entityManager.findObjectsById(end.id)?.[0] if (line instanceof Line2) { const geom = line.geometry geom.setFromPoints([startPoint.position, endPoint.position]) } }) } /** * 删除一根线 * @param start 起点 * @param end 终点 * @param option 渲染选项 */ deleteLine(start: ItemJson, end: ItemJson, option?: RendererCudOption) { const id = getLineId(start.id, end.id, 'center') const lines = this.tempViewport.entityManager.findLineObjectsById(id) if (lines) { this.tempViewport.scene.remove(...lines) } this.tempViewport.entityManager.deleteLineObjectOnly(id) } /** * 结束更新 */ endUpdate(): void { this.tempViewport = undefined } }