diff --git a/src/assets/images/moveline_point.png b/src/assets/images/moveline_point.png new file mode 100644 index 0000000..f1468d4 Binary files /dev/null and b/src/assets/images/moveline_point.png differ diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index 2d452b9..0fa88cc 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -192,7 +192,9 @@ export function findStateItemsByDistance(viewport: Viewport, point: Vector2, dis export function quickCopyByMouse() { // 获取鼠标位置,查看鼠标是否在某个 viewport 的画布上,并取得该 viewport - if (!CurrentMouseInfo?.viewport || !CurrentMouseInfo.x || !CurrentMouseInfo.z) { + if (!CurrentMouseInfo?.viewport || + isNaN(CurrentMouseInfo.x) || isNaN(CurrentMouseInfo.z) || + isNaN(CurrentMouseInfo.x) || isNaN(CurrentMouseInfo.z)) { system.msg('无法获取鼠标位置') return } diff --git a/src/core/base/BaseInteraction.ts b/src/core/base/BaseInteraction.ts index 84595a9..6082099 100644 --- a/src/core/base/BaseInteraction.ts +++ b/src/core/base/BaseInteraction.ts @@ -92,7 +92,7 @@ export default abstract class BaseInteraction { if (this.viewport !== viewport) return // 获取当前鼠标所在位置 - if (!CurrentMouseInfo || !CurrentMouseInfo.x || !this.dragItem?.tf?.[0]) { + if (!CurrentMouseInfo || isNaN(CurrentMouseInfo.x) || isNaN(CurrentMouseInfo.z) || !this.dragItem?.tf?.[0]) { return } @@ -297,8 +297,8 @@ export default abstract class BaseInteraction { } else { const renderer = getRenderer(this.itemTypeName) - const defaultScale = renderer.getDefaultScale() - const defaultRotation = renderer.getDefaultRotation() + const defaultScale = renderer.defaultScale + const defaultRotation = renderer.defaultRotation // 添加正式点 catchPoint = { @@ -349,8 +349,8 @@ export default abstract class BaseInteraction { createTempPointMarker(position?: THREE.Vector3): THREE.Mesh { const p = position const renderer = getRenderer(this.itemTypeName) - const scale = renderer.getDefaultScale() - const rotation = renderer.getDefaultRotation() + const scale = renderer.defaultScale + const rotation = renderer.defaultRotation const geometry = new THREE.BoxGeometry(1, 1, 1) const material = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 }) diff --git a/src/core/base/BaseRenderer.ts b/src/core/base/BaseRenderer.ts index 1533c89..0bce096 100644 --- a/src/core/base/BaseRenderer.ts +++ b/src/core/base/BaseRenderer.ts @@ -54,9 +54,9 @@ export default abstract class BaseRenderer { */ abstract createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D[] - abstract getDefaultScale(): THREE.Vector3 + abstract get defaultScale(): THREE.Vector3 - abstract getDefaultRotation(): THREE.Vector3 + abstract get defaultRotation(): THREE.Vector3 /** * 创建或更新线之后的回调 @@ -65,6 +65,12 @@ export default abstract class BaseRenderer { } /** + * 创建或更新点之后的回调 + */ + afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, objects: THREE.Object3D[]) { + } + + /** * 删除线之后的回调 */ afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, objects: THREE.Object3D[]) { @@ -147,6 +153,7 @@ export default abstract class BaseRenderer { point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2]) }) this.fillObjectUserDataFromItem(item, ...points) + this.afterCreateOrUpdatePoint(item, option, points) this.tempViewport.entityManager.appendObject(item.id, points) this.appendToScene(...points) @@ -194,6 +201,9 @@ export default abstract class BaseRenderer { point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2]) }) + + this.fillObjectUserDataFromItem(item, ...objects) + this.afterCreateOrUpdatePoint(item, option, objects) } /** diff --git a/src/core/controls/MouseMoveInspect.ts b/src/core/controls/MouseMoveInspect.ts index 8ed262e..0eb53bc 100644 --- a/src/core/controls/MouseMoveInspect.ts +++ b/src/core/controls/MouseMoveInspect.ts @@ -36,11 +36,11 @@ export default class MouseMoveInspect implements IControls { } mouseLv() { - this.viewport.state.mouse.x = 0 - this.viewport.state.mouse.z = 0 + this.viewport.state.mouse.x = NaN + this.viewport.state.mouse.z = NaN window['CurrentMouseInfo'] = { - x: 0, - z: 0 + x: NaN, + z: NaN } } diff --git a/src/core/engine/SceneHelp.ts b/src/core/engine/SceneHelp.ts index 6ca655d..f120353 100644 --- a/src/core/engine/SceneHelp.ts +++ b/src/core/engine/SceneHelp.ts @@ -1,5 +1,8 @@ import * as THREE from 'three' import type WorldModel from '@/core/manager/WorldModel' +import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry' +import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial' +import { Line2 } from 'three/examples/jsm/lines/Line2' /** * 场景帮助类 @@ -7,7 +10,7 @@ import type WorldModel from '@/core/manager/WorldModel' */ export default class SceneHelp { scene: THREE.Scene - axesHelper: THREE.GridHelper + axesHelper: THREE.Group gridHelper: THREE.GridHelper worldModel: WorldModel catalogCode: string @@ -21,26 +24,18 @@ export default class SceneHelp { this.worldModel = worldModel this.catalogCode = catalogCode + // 辅助线 + const gridOption = this.worldModel.gridOption + // 初始化 Three.js 场景 this.scene = new THREE.Scene() - this.scene.background = new THREE.Color(0xeeeeee) + this.scene.background = new THREE.Color(gridOption.backgroundColor) - // 辅助线 - const gridOption = this.worldModel.gridOption - const axesHelper = new THREE.GridHelper(gridOption.axesSize, gridOption.axesDivisions) - axesHelper.material.color.setHex(gridOption.axesColor) - axesHelper.material.linewidth = 2 - axesHelper.material.opacity = gridOption.gridOpacity - axesHelper.material.transparent = true - if (!gridOption.axesEnabled) { - axesHelper.visible = false + if (gridOption.axesEnabled) { + this.axesHelper = createAxes(gridOption.axesSize, gridOption.axesColor, gridOption.axesWidth) + this.scene.add(this.axesHelper) } - // @ts-ignore - axesHelper.material.vertexColors = false - this.axesHelper = axesHelper - this.scene.add(this.axesHelper) - const gridHelper = new THREE.GridHelper(gridOption.gridSize, gridOption.gridDivisions) gridHelper.material.color.setHex(gridOption.gridColor) gridHelper.material.opacity = gridOption.gridOpacity @@ -136,4 +131,35 @@ export default class SceneHelp { this.scene.children = [] this.scene = null } +} + + +function createAxes(axesSize = 5, axesColor = 0x000000, axesWidth = 2) { + // 创建四个方向的线条几何体 + const positions = [ + // X 轴(水平) + [new THREE.Vector3(0, 0, 0), new THREE.Vector3(axesSize, 0, 0)], // 向东 + [new THREE.Vector3(0, 0, 0), new THREE.Vector3(-axesSize, 0, 0)], // 向西 + // Y 轴(垂直) + [new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, axesSize)], // 向北 + [new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -axesSize)] // 向南 + ] + + const group = new THREE.Group() + positions.forEach(([start, end]) => { + const geometry = new LineGeometry() + const pa = new Float32Array([ + start.x, start.y, start.z, + end.x, end.y, end.z + ]) + geometry.setPositions(pa) + const material = new LineMaterial({ + color: axesColor, + linewidth: axesWidth, + vertexColors: false + }) + const line = new Line2(geometry, material) + group.add(line) + }) + return group } \ No newline at end of file diff --git a/src/core/engine/Viewport.ts b/src/core/engine/Viewport.ts index b0c03fb..ba912ff 100644 --- a/src/core/engine/Viewport.ts +++ b/src/core/engine/Viewport.ts @@ -350,10 +350,6 @@ export default class Viewport { return this.scene.worldModel } - get axesHelper(): THREE.GridHelper { - return this.scene.axesHelper - } - get gridHelper(): THREE.GridHelper { return this.scene.gridHelper } @@ -521,4 +517,4 @@ export interface ViewportState { x: number, z: number } -} \ No newline at end of file +} diff --git a/src/core/manager/ModuleManager.ts b/src/core/manager/ModuleManager.ts index 09437b4..85ad441 100644 --- a/src/core/manager/ModuleManager.ts +++ b/src/core/manager/ModuleManager.ts @@ -23,12 +23,12 @@ window['modules'] = modules /** * 模块管理器 */ -export function defineModule(option: ModuleDefineOption): void { +export function defineModule(option: ModuleDefineOption): Promise { if (modules.has(option.name)) { throw new Error(`Module with name "${option.name}" is already defined.`) } modules.set(option.name, option) - option.renderer.init().finally() + return option.renderer.init() } /** diff --git a/src/core/manager/WorldModel.ts b/src/core/manager/WorldModel.ts index e9b0e24..446fd8b 100644 --- a/src/core/manager/WorldModel.ts +++ b/src/core/manager/WorldModel.ts @@ -1,7 +1,8 @@ import _ from 'lodash' import { reactive, watch } from 'vue' import EventBus from '@/runtime/EventBus' -import localforage from 'localforage' +import Measure from '@/modules/measure' +import Way from '@/modules/way' import StateManager from '@/core/manager/StateManager.ts' export interface WorldModelState { @@ -33,16 +34,17 @@ export default class WorldModel { const data = _.get(this.data, 'Tool.gridHelper') return _.defaultsDeep(data, { axesEnabled: true, - axesSize: 1000, - axesDivisions: 4, - axesColor: 0x000000, - axesOpacity: 1, + axesSize: 5, + axesColor: 0xDDDDDD, + axesWidth: 2, gridEnabled: true, // 启用网格 gridSize: 1000, // 网格大小, 单位米 gridDivisions: 1000, // 网格分割数 - gridColor: 0x999999, // 网格颜色, 十六进制颜色值 + gridColor: 0xDDDDDD, // 网格颜色, 十六进制颜色值 gridOpacity: 0.8, // 网格透明度 + + backgroundColor: 0xF5F5F5, // 背景颜色, 十六进制颜色值 snapEnabled: true, // 启用吸附 snapDistance: 0.25 // 吸附距离, 单位米 }) @@ -59,7 +61,8 @@ export default class WorldModel { watch(() => this.state.catalogCode, this.onCatalogCodeChanged.bind(this)) return Promise.all([ - import('@/modules/measure') + Measure, + Way ]).then(() => { console.log('世界模型初始化完成') diff --git a/src/example/example1.js b/src/example/example1.js index 0bda2ee..036ba9c 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -14,17 +14,17 @@ export default { ], gridHelper: { // 网格辅助线 axesEnabled: true, // 是否显示中心轴 - axesSize: 50, // 中心轴长度 - axesDivisions: 2, // 中心轴分割数 - axesColor: 0x000000, // 中心轴颜色 - axesOpacity: 1, // 中心轴透明度 + axesSize: 5, + axesColor: 0xC7C7C7, + axesWidth: 2, gridEnabled: true, // 是否显示网格 gridSize: 1000, // 网格大小 gridDivisions: 1000, // 网格分割数 - gridColor: 0x999999, // 网格颜色 - gridOpacity: 0.8, // 网格透明度 + gridColor: 0xC7C7C7, // 网格颜色 + gridOpacity: 1, // 网格透明度 + backgroundColor: 0x24292E, // 背景颜色 snapEnabled: true, // 是否启用吸附 snapDistance: 0.25 // 吸附距离 } @@ -82,8 +82,8 @@ export default { v: true, tf: [ [-4, 0.1, 4.75], - [0, 0, 0], - [0.25, 0.1, 0.25] + [90, 0, 0], + [0.25, 0.25, 0.1] ], dt: { in: [], @@ -96,8 +96,8 @@ export default { v: true, tf: [ [5, 0.1, 2.75], - [0, 0, 0], - [0.25, 0.1, 0.25] + [90, 0, 0], + [0.25, 0.25, 0.1] ], dt: { in: [], @@ -110,8 +110,8 @@ export default { v: true, tf: [ [5, 0.1, 5.75], - [0, 0, 0], - [0.25, 0.1, 0.25] + [90, 0, 0], + [0.25, 0.25, 0.1] ], dt: { in: [], @@ -124,8 +124,8 @@ export default { v: true, tf: [ [-1.25, 0.1, 7.25], - [0, 0, 0], - [0.25, 0.1, 0.25] + [90, 0, 0], + [0.25, 0.25, 0.1] ], dt: { in: [], @@ -138,8 +138,8 @@ export default { v: true, tf: [ [-2, 0.1, 6], - [0, 0, 0], - [0.25, 0.1, 0.25] + [90, 0, 0], + [0.25, 0.25, 0.1] ], dt: { in: [], @@ -152,8 +152,8 @@ export default { v: true, tf: [ [-3.5, 0.1, 5.25], - [0, 0, 0], - [0.25, 0.1, 0.25] + [90, 0, 0], + [0.25, 0.25, 0.1] ], dt: { in: [], diff --git a/src/modules/measure/MeasureRenderer.ts b/src/modules/measure/MeasureRenderer.ts index 3e5135e..3f6d7b3 100644 --- a/src/modules/measure/MeasureRenderer.ts +++ b/src/modules/measure/MeasureRenderer.ts @@ -25,35 +25,52 @@ export default class MeasureRenderer extends BaseRenderer { public useHtmlLabel = false - pointMaterial: THREE.MeshBasicMaterial + pointMaterial: THREE.Material lineMaterial: LineMaterial - readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.1, 0.25) - readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.25, 0.1) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0) constructor(itemTypeName: string) { super(itemTypeName) } async init() { - this.pointMaterial = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 }) + this.pointMaterial = new THREE.SpriteMaterial({ + color: 0xFFFF99, // 0x303133, + transparent: true, + side: THREE.DoubleSide, + opacity: 1, + sizeAttenuation: true + }) this.lineMaterial = new LineMaterial({ - color: 0xE63C17, // 主颜色 - linewidth: 2, // 实际可用的线宽 - vertexColors: true, // 启用顶点颜色 - dashed: false, - alphaToCoverage: true + color: 0xFF8C00, + linewidth: 1, + vertexColors: false, + dashed: false }) return Promise.all([ - super.init(), - this.loadFont() + super.init() ]) } - async loadFont() { - return Promise.resolve() + /** + * 所有的点,必须使用同一个尺寸, 改属性也无效 + */ + 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) @@ -65,11 +82,16 @@ export default class MeasureRenderer extends BaseRenderer { } createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { - const tt = new THREE.BoxGeometry(1, 1, 1) - const obj = new THREE.Mesh(tt, this.pointMaterial) + // 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 - obj.uuid = item.id - return [obj] } @@ -102,7 +124,7 @@ export default class MeasureRenderer extends BaseRenderer { const p1 = endPoint.position const dist = p0.distanceTo(p1) - const label = `${numberToString(dist)} m` + 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 @@ -161,7 +183,7 @@ export default class MeasureRenderer extends BaseRenderer { label.text = text label.font = SimSunTTF label.fontSize = 0.4 - label.color = '#ff0000' + label.color = '#333333' label.opacity = 0.8 label.padding = 0.2 label.anchorX = 'center' @@ -178,14 +200,6 @@ export default class MeasureRenderer extends BaseRenderer { } } - getDefaultScale(): THREE.Vector3 { - return this.defaultScale - } - - getDefaultRotation(): THREE.Vector3 { - return this.defaultRotation - } - dispose() { super.dispose() diff --git a/src/modules/measure/index.ts b/src/modules/measure/index.ts index 03e515a..0bbcc8d 100644 --- a/src/modules/measure/index.ts +++ b/src/modules/measure/index.ts @@ -6,7 +6,7 @@ import MeasureInteraction from './MeasureInteraction.ts' export const ITEM_TYPE_NAME = 'measure' -defineModule({ +export default defineModule({ name: ITEM_TYPE_NAME, renderer: new MeasureRenderer(ITEM_TYPE_NAME), interaction: new MeasureInteraction(ITEM_TYPE_NAME), diff --git a/src/modules/way/WayEntity.ts b/src/modules/way/WayEntity.ts new file mode 100644 index 0000000..0d4462c --- /dev/null +++ b/src/modules/way/WayEntity.ts @@ -0,0 +1,5 @@ +import BaseEntity from '@/core/base/BaseItemEntity.ts' + +export default class WayEntity extends BaseEntity { + +} \ No newline at end of file diff --git a/src/modules/way/WayInteraction.ts b/src/modules/way/WayInteraction.ts new file mode 100644 index 0000000..7d1f48c --- /dev/null +++ b/src/modules/way/WayInteraction.ts @@ -0,0 +1,8 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' + +export default class WayInteraction extends BaseInteraction { + + constructor(itemTypeName: string) { + super(itemTypeName) + } +} \ No newline at end of file diff --git a/src/modules/way/WayMeta.ts b/src/modules/way/WayMeta.ts new file mode 100644 index 0000000..2273048 --- /dev/null +++ b/src/modules/way/WayMeta.ts @@ -0,0 +1,14 @@ +import type { IMeta } from '@/core/base/IMeta.ts' + +export default [ + { field: 'uuid', editor: 'UUID', label: 'uuid', readonly: true, category: 'basic' }, + { field: 'name', editor: 'TextInput', label: '名称', category: 'basic' }, + { field: 'dt.label', editor: 'TextInput', label: '标签', category: 'basic' }, + { editor: 'TransformEditor', category: 'basic' }, + { field: 'dt.color', editor: 'Color', label: '颜色', category: 'basic' }, + { editor: '-', category: 'basic' }, + { field: 'tf', editor: 'InOutCenterEditor', category: 'basic' }, + { field: 'dt.selectable', editor: 'Switch', label: '可选中', category: 'basic' }, + { field: 'dt.protected', editor: 'Switch', label: '受保护', category: 'basic' }, + { field: 'visible', editor: 'Switch', label: '可见', category: 'basic' } +] as IMeta \ No newline at end of file diff --git a/src/modules/way/WayRenderer.ts b/src/modules/way/WayRenderer.ts new file mode 100644 index 0000000..878f4aa --- /dev/null +++ b/src/modules/way/WayRenderer.ts @@ -0,0 +1,199 @@ +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' +import MoveLinePointPng from '@/assets/images/moveline_point.png' + +/** + * 辅助测量工具渲染器 + */ +export default class WayRenderer extends BaseRenderer { + static LABEL_NAME = 'way_label' + static POINT_NAME = 'way_point' + static LINE_NAME = 'way_line' + + public useHtmlLabel = false + + pointMaterial: THREE.Material + lineMaterial: LineMaterial + + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.5, 0.5, 0.1) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0) + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + async init() { + this.lineMaterial = new LineMaterial({ + color: 0xE63C17, // 主颜色 + linewidth: 2, // 实际可用的线宽 + vertexColors: true, // 启用顶点颜色 + dashed: false, + alphaToCoverage: true + }) + return Promise.all([ + super.init(), + this.loadFont() + ]) + } + + async loadFont() { + return new Promise((resolve, reject) => { + new THREE.TextureLoader().load( + MoveLinePointPng, + (texture) => { + this.pointMaterial = new THREE.SpriteMaterial({ + map: texture, + transparent: true, + side: THREE.DoubleSide + }) + resolve() + }, + undefined, + function(err) { + reject(err) + } + ) + }) + } + + /** + * 所有的点,必须使用同一个尺寸, 改属性也无效 + */ + 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 = WayRenderer.LINE_NAME + obj.uuid = getLineId(start.id, end.id, type) + + return [obj] + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { + const obj = new THREE.Sprite(this.pointMaterial as THREE.SpriteMaterial) + obj.name = WayRenderer.POINT_NAME + return [obj] + } + + appendToScene(...objects: THREE.Object3D[]) { + const dragObjects = objects.filter(obj => !!obj.userData.draggable) + this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') + // this.tempViewport.dragControl.setDragObjects(objects, 'remove') + + this.tempViewport?.scene.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.appendToScene(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 = WayRenderer.LABEL_NAME + return obj + + } else { + const label = new Text() + label.text = text + label.font = SimSunTTF + label.fontSize = 0.4 + label.color = '#ff0000' + 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 = WayRenderer.LABEL_NAME + + label.sync() + return label + } + } + + dispose() { + super.dispose() + this.pointMaterial.dispose() + this.lineMaterial.dispose() + } +} \ No newline at end of file diff --git a/src/modules/way/index.ts b/src/modules/way/index.ts new file mode 100644 index 0000000..5d05990 --- /dev/null +++ b/src/modules/way/index.ts @@ -0,0 +1,15 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import WayRenderer from './WayRenderer.ts' +import WayEntity from './WayEntity.ts' +import WayMeta from './WayMeta.ts' +import WayInteraction from './WayInteraction.ts' + +export const ITEM_TYPE_NAME = 'way' + +export default defineModule({ + name: ITEM_TYPE_NAME, + renderer: new WayRenderer(ITEM_TYPE_NAME), + interaction: new WayInteraction(ITEM_TYPE_NAME), + meta: WayMeta, + entity: WayEntity +}) \ No newline at end of file diff --git a/src/types/model.d.ts b/src/types/model.d.ts index bc63beb..9d8cdca 100644 --- a/src/types/model.d.ts +++ b/src/types/model.d.ts @@ -57,17 +57,13 @@ interface IGridHelper { */ axesSize: number; /** - * 坐标轴分割数 - */ - axesDivisions: number; - /** * 坐标轴颜色, 十六进制颜色值 */ axesColor: number; /** * 坐标轴透明度 */ - axesOpacity: number; + axesWidth: number; /** * 启用网格 @@ -91,6 +87,11 @@ interface IGridHelper { gridOpacity: number; /** + * 背景颜色, 十六进制颜色值 + */ + backgroundColor: number; + + /** * 启用吸附 */ snapEnabled: boolean;