diff --git a/doc/物流模型总体介绍.md b/doc/物流模型总体介绍.md index 31f8fcd..249b83c 100644 --- a/doc/物流模型总体介绍.md +++ b/doc/物流模型总体介绍.md @@ -65,11 +65,8 @@ ### 物流世界 一个物流仓库, 就是一个世界 - 他有自己的项目定义, 楼层, 围墙, 柱子, 其他数据, 个性化脚本等等 - 每次对建模文件打开的时候, 因为性能问题, 一次只会读取一个楼层, 或者一个水平横截面. - 因此, 一个 floor 就是对应一个 THREE.Scene ```ts diff --git a/src/core/ModelUtils.ts b/src/core/ModelUtils.ts index 4ea6e35..b8a7650 100644 --- a/src/core/ModelUtils.ts +++ b/src/core/ModelUtils.ts @@ -6,6 +6,18 @@ import { computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh' import { Vector2 } from 'three/src/math/Vector2' import type Toolbox from '@/model/itemType/Toolbox.ts' +export function getLineId(startId: string, endId: string, type: LinkType): string { + if (type === 'center') { + // 无序线, start / end 大的在前 + if (startId > endId) { + return `${type}_${endId}_${startId}` + } + } + // 其他的线是有序线 + // 线条必须加上 type, 因为 center 与 in/out 是可以并存的, 他们类型不一样 + return `${type}_${startId}_${endId}` +} + export function deletePointByKeyboard() { system.msg('Delete not impleted yet') // const viewport: Viewport = window['viewport'] diff --git a/src/core/base/BaseRenderer.ts b/src/core/base/BaseRenderer.ts index 4e50a48..edcbf28 100644 --- a/src/core/base/BaseRenderer.ts +++ b/src/core/base/BaseRenderer.ts @@ -1,38 +1,121 @@ 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 { - // Optional: Pause animations or prepare for batch updates + 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 渲染选项 */ - abstract createPoint(item: ItemJson, option?: RendererCudOption): void + 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 渲染选项 */ - abstract deletePoint(id: string, option?: RendererCudOption): void + 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 渲染选项 */ - abstract updatePoint(item: ItemJson, option?: RendererCudOption): void + 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]) + }) + } /** * 创建一根线 @@ -41,12 +124,30 @@ export default abstract class BaseRenderer { * @param type 线的类型 * @param option 渲染选项 */ - abstract createLine( - start: ItemJson, - end: ItemJson, - type: 'in' | 'out' | 'center', - option?: RendererCudOption - ): void + 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) + } /** * 更新一根线 @@ -55,12 +156,20 @@ export default abstract class BaseRenderer { * @param type 线的类型 * @param option 渲染选项 */ - abstract updateLine( - start: ItemJson, - end: ItemJson, - type: 'in' | 'out' | 'center', - option?: RendererCudOption - ): void + 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]) + } + }) + } /** * 删除一根线 @@ -68,18 +177,21 @@ export default abstract class BaseRenderer { * @param end 终点 * @param option 渲染选项 */ - abstract deleteLine( - start: ItemJson, - end: ItemJson, - option?: RendererCudOption - ): void + 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) + } /** * 结束更新 - * @param viewport 当前视口 */ - endUpdate(viewport: Viewport): void { - // Optional: Resume animations or finalize batch updates + endUpdate(): void { + this.tempViewport = undefined } } diff --git a/src/core/engine/SceneHelp.ts b/src/core/engine/SceneHelp.ts index fff615c..e5e90fd 100644 --- a/src/core/engine/SceneHelp.ts +++ b/src/core/engine/SceneHelp.ts @@ -78,8 +78,14 @@ export default class SceneHelp { // }) // } - remove(...object: THREE.Object3D[]) { - this.scene.remove(...object) + remove(...objects: THREE.Object3D[]) { + _.forEach(objects, (object) => { + if (object?.parent) { + object.parent.remove(object) + } else { + this.scene.remove(object) + } + }) } add(...object: THREE.Object3D[]) { diff --git a/src/core/engine/Viewport.ts b/src/core/engine/Viewport.ts index 1b9571b..0a82ef4 100644 --- a/src/core/engine/Viewport.ts +++ b/src/core/engine/Viewport.ts @@ -21,8 +21,8 @@ import { calcPositionUseSnap } from '@/core/ModelUtils' import StateManager from '@/core/manager/StateManager.ts' /** - * 编辑器对象 - * 这是非双向绑定的设计器对象,不记录状态,只记录全局使用到的对象,(实体类使用) + * 视窗对象 + * 所有状态管理器,场景,控制器,摄像机,实体管理器, 都在这里可以取到 */ export default class Viewport { viewerDom: HTMLElement @@ -49,41 +49,10 @@ export default class Viewport { // 交互管理器 interactionManager = new InteractionManager() - beginSync() { - } - - syncOnRemove(id: string) { - } - - syncOnUpdate(item: VDataItem) { - } - - syncOnAppend(item: VDataItem) { - } - - endSync() { - } - - get worldModel(): WorldModel { - return this.scene.worldModel - } - - get axesHelper(): THREE.GridHelper { - return this.scene.axesHelper - } - - get gridHelper(): THREE.GridHelper { - return this.scene.gridHelper - } - - /** - * 监听窗口大小变化 - */ + // 监听窗口大小变化 resizeObserver?: ResizeObserver - /** - * vue 的 watcher - */ + // vue 的 watcher 管理器, 卸载时需要调用 watchList: (() => void)[] = [] css2DRenderer: CSS2DRenderer = new CSS2DRenderer() @@ -92,6 +61,7 @@ export default class Viewport { //@ts-ignore state: ViewportState = reactive({ isReady: false, + isUpdating: false, cursorMode: 'normal', selectedObject: null, camera: { @@ -275,7 +245,7 @@ export default class Viewport { this.animationFrameId = requestAnimationFrame(this.animate.bind(this)) this.renderView() - if(window['lineMaterial']) { + if (window['lineMaterial']) { this.offset -= 0.002 window['lineMaterial'].dashOffset = this.offset } @@ -346,6 +316,26 @@ export default class Viewport { } } + beginUpdate() { + this.state.isUpdating = true + } + + endUpdate() { + this.state.isUpdating = false + } + + get worldModel(): WorldModel { + return this.scene.worldModel + } + + get axesHelper(): THREE.GridHelper { + return this.scene.axesHelper + } + + get gridHelper(): THREE.GridHelper { + return this.scene.gridHelper + } + /** * 根据可视化范围更新网格的透明度 */ @@ -438,11 +428,6 @@ export default class Viewport { export interface ViewportState { /** - * 当前楼层 - */ - currentFloor: string - - /** * 是否准备完成 */ isReady: boolean @@ -463,6 +448,11 @@ export interface ViewportState { selectedObjectMeta: ItemTypeMeta | null /** + * 是否正在更新中 + */ + isUpdating: boolean + + /** * 相机状态 */ camera: { diff --git a/src/core/manager/EntityManager.ts b/src/core/manager/EntityManager.ts index 2c8099e..ec9246b 100644 --- a/src/core/manager/EntityManager.ts +++ b/src/core/manager/EntityManager.ts @@ -1,37 +1,40 @@ import * as THREE from 'three' +import type Viewport from '@/core/engine/Viewport' +import type BaseRenderer from '@/core/base/BaseRenderer' import { getRenderer } from './ModuleManager' -import type Viewport from '@/core/engine/Viewport.ts' +import { getLineId } from '@/core/ModelUtils' /** - * 缓存所有实体和他们的关系, 在各个组件的渲染器会调用这个实体管理器, 进行检索 / 关系 / 获取差异等计算 + * 实体管理器 + * 缓存所有 数据(ItemJson)和他们的关系, 以及渲染对象 THREE.Object3D + * 数据更新的流程是: + * 1. 状态管理器 StateManager 发出数据被修改的请求, 这些请求通常只有 ItemJson 数据 + * 2. 实体管理器 (EntityManager) 需要计算所有相关的点和线的变化. 在 endUpdate 时需要根据 center / in / out 关系, 计算出: 点的创建 / 点的更新 / 点的删除 / 线的创建 / 线的更新 / 线的删除 + * 3. 通知对应的渲染器 (Renderer) 进行点和线的创建 / 更新 / 删除 */ export default class EntityManager { - /** - * 视窗对象, 所有状态管理器, ThreeJs场景,控制器,摄像机, 实体管理器都在这里 - */ viewport: Viewport - /** - * 所有数据点的实体 - */ - entities = new Map() + // 所有数据点的实体 + readonly entities = new Map() - /** - * 所有数据点与 THREEJS 对象的关系 - */ - objects = new Map() + // 关系索引 + readonly relationIndex = new Map() - /** - * 所有关联关系 - */ - relationIndex = new Map; in: Set; out: Set }>() + // 所有 THREEJS "点"对象 + readonly objects = new Map() - /** - * 两两关联关系与 THREEJS 对象之间的关联 - */ - lines = new Map() + // 所有 THREEJS "线"对象 + readonly lines = new Map() - private batchMode = false + // 差量渲染器 + readonly diffRenderer = new Map() + // 线差量记录 + lineDiffs = { + create: new Map(), + update: new Map(), + delete: new Map() + } init(viewport: Viewport) { this.viewport = viewport @@ -41,48 +44,51 @@ export default class EntityManager { * 批量更新开始 */ beginUpdate(): void { - this.batchMode = true - this.viewport.beginSync() + this.viewport.beginUpdate() + this.diffRenderer.clear() + this.lineDiffs.create.clear() + this.lineDiffs.update.clear() + this.lineDiffs.delete.clear() } /** - * 创建一个实体, 这个点的 center[] / in[] / out[] 关联的点, 都要对应进行关联 + * 创建或更新一个实体, 这个点的 center[] / in[] / out[] 关联的点, 可能都要对应进行关联 */ - createEntity(entity: ItemJson, option?: EntityCudOption): void { - if (this.entities.has(entity.id!)) { - throw new Error(`Entity with ID "${entity.id}" already exists.`) + createOrUpdateEntity(entity: ItemJson, option: EntityCudOption = {}): void { + if (!entity?.id) { + throw new Error('Entity must have an id') } - this.entities.set(entity.id!, entity) - this.updateRelations(entity) - const renderer = getRenderer(entity.t) - renderer.createPoint(entity, option) - } - /** - * 更新实体, 他可能更新位置, 也可能更新颜色, 也可能修改 dt.center[] / dt.in[] / dt.out[] 修正与其他点之间的关联 - */ - updateEntity(entity: ItemJson, option?: EntityCudOption): void { - if (!this.entities.has(entity.id!)) { - throw new Error(`Entity with ID "${entity.id}" does not exist.`) + // 找到这个数据的渲染器 + const renderer = this.getDiffRenderer(entity.t) + + const originEntity = this.entities.get(entity.id) + this.entities.set(entity.id, entity) + + // 更新关系网 + this.updateRelations(entity, originEntity) + + if (typeof originEntity === 'undefined') { + renderer.createPoint(entity, option) + + } else { + option.originEntity = _.cloneDeep(originEntity) + renderer.updatePoint(entity, option) } - this.entities.set(entity.id!, entity) - this.updateRelations(entity) - const renderer = getRenderer(entity.t) - renderer.updatePoint(entity, option) } /** * 删除实体, 与这个点有关的所有线都要删除, 与这个点有关系的点,都要对应的 center[] / in[] / out[] 都要断绝关系 */ - deleteEntity(id: string, option?: EntityCudOption): void { + deleteEntity(id: string, option: EntityCudOption = {}): void { const entity = this.entities.get(id) - if (!entity) { - throw new Error(`Entity with ID "${id}" does not exist.`) - } + if (!entity) return + + option.originEntity = _.cloneDeep(entity) + this.entities.delete(id) this.removeRelations(id) - const renderer = getRenderer(entity.t) - renderer.deletePoint(id, option) + this.getDiffRenderer(entity.t).deletePoint(id, option) } /** @@ -92,66 +98,216 @@ export default class EntityManager { * - 如果进行了删除, 与这个点有关的所有线都要删除, 与这个点有关系的点,都要对应的 center[] / in[] / out[] 都要断绝关系 * - 如果进行了更新, 如果改了颜色/位置, 则需要在UI上进行对应修改,如果改了关系,需要与关联的节点批量调整 * 将影响到的所有数据, 都变成一个修改集合, 统一调用对应单元类型渲染器(BaseRenderer)的 createPoint / deletePoint / updatePoint / createLine / updateLine / deleteLine 方法 - * 具体方法就是 viewport.getItemTypeRenderer(itemTypeName) */ commitUpdate(): void { - this.batchMode = false - this.viewport.endSync() - } + for (const [itemTypeName, renderer] of this.diffRenderer.entries()) { + for (const [lineId, lineDiffItem] of this.lineDiffs.create.entries()) { + const start = this.entities.get(lineDiffItem.startId) + const end = this.entities.get(lineDiffItem.endId) + if (start.t !== itemTypeName) { + // 只通知起点对应的渲染器 + continue + } + renderer.createLine(start, end, lineDiffItem.type) + } - /** - * 获取实体 - */ - getEntity(id: string): ItemJson | undefined { - return this.entities.get(id) + for (const [lineId, lineDiffItem] of this.lineDiffs.update.entries()) { + const start = this.entities.get(lineDiffItem.startId) + const end = this.entities.get(lineDiffItem.endId) + if (start.t !== itemTypeName) { + // 只通知起点对应的渲染器 + continue + } + renderer.updateLine(start, end, lineDiffItem.type) + } + + for (const [lineId, lineDiffItem] of this.lineDiffs.delete.entries()) { + const start = this.entities.get(lineDiffItem.startId) + const end = this.entities.get(lineDiffItem.endId) + if (start.t !== itemTypeName) { + // 只通知起点对应的渲染器 + continue + } + renderer.deleteLine(start, end, lineDiffItem.type) + } + + renderer.endUpdate() + } + this.viewport.endUpdate() } /** - * 获取相关实体 + * 更新关系关系网, 计算出差值, 可以临时放在 diffRenderer 中, 等待 commitUpdate 时统一处理 */ - getRelatedEntities(id: string, relationType: 'center' | 'in' | 'out'): ItemJson[] { - const relations = this.relationIndex.get(id)?.[relationType] || new Set() - return Array.from(relations).map((relatedId) => this.entities.get(relatedId)!) - } - - private updateRelations(entity: ItemJson): void { + private updateRelations(entity: ItemJson, originEntity?: ItemJson): void { const { id, dt } = entity if (!id || !dt) return - const relations = this.relationIndex.get(id) || { center: new Set(), in: new Set(), out: new Set() } - relations.center = new Set(dt.center || []) - relations.in = new Set(dt.in || []) - relations.out = new Set(dt.out || []) + const oldCenter = new Set(originEntity?.dt?.center || []) + const oldIn = new Set(originEntity?.dt?.in || []) + const oldOut = new Set(originEntity?.dt?.out || []) + + const newCenter = new Set(dt.center || []) + const newIn = new Set(dt.in || []) + const newOut = new Set(dt.out || []) + + // 更新正向关系 + const relations = this.relationIndex.get(id) || new Relation() + relations.center = newCenter + relations.input = newIn + relations.output = newOut this.relationIndex.set(id, relations) - // Update reverse relations - this.updateReverseRelations(id, dt.center, 'center') - this.updateReverseRelations(id, dt.in, 'out') - this.updateReverseRelations(id, dt.out, 'in') + // 更新反向关系 + this.updateReverseRelations(id, oldCenter, newCenter, 'center') + this.updateReverseRelations(id, oldIn, newIn, 'out') // 入边的反向是出边 + this.updateReverseRelations(id, oldOut, newOut, 'in') // 出边的反向是入边 + + // 更新线差量 + this.calculateLineDiffs(id, oldCenter, newCenter, 'center') + this.calculateLineDiffs(id, oldIn, newIn, 'in') + this.calculateLineDiffs(id, oldOut, newOut, 'out') } - private updateReverseRelations(id: string, relatedIds: string[] | undefined, relationType: 'center' | 'in' | 'out'): void { - if (!relatedIds) return - relatedIds.forEach((relatedId) => { - const relatedRelations = this.relationIndex.get(relatedId) || { - center: new Set(), - in: new Set(), - out: new Set() + private updateReverseRelations(id: string, oldIds: Set, newIds: Set, relationType: LinkType) { + // 移除旧关系 + for (const relatedId of oldIds) { + if (!newIds.has(relatedId)) { + const rev = this.relationIndex.get(relatedId) + rev.delete(relationType, id) } - relatedRelations[relationType].add(id) - this.relationIndex.set(relatedId, relatedRelations) - }) + } + + // 添加新关系 + for (const relatedId of newIds) { + if (!oldIds.has(relatedId)) { + let rev = this.relationIndex.get(relatedId) + if (!rev) { + rev = new Relation() + this.relationIndex.set(relatedId, rev) + } + rev.add(relationType, id) + } + } } + private calculateLineDiffs(id: string, oldIds: Set, newIds: Set, lineType: LinkType) { + // 删除被移除的线 + for (const relatedId of oldIds) { + if (!newIds.has(relatedId)) { + const lineId = getLineId(id, relatedId, lineType) + this.lineDiffs.delete.set(lineId, { startId: id, endId: relatedId, type: lineType }) + } + } + + // 新增新增的线 + for (const relatedId of newIds) { + if (!oldIds.has(relatedId)) { + const lineId = getLineId(id, relatedId, lineType) + this.lineDiffs.create.set(lineId, { startId: id, endId: relatedId, type: lineType }) + } + } + } + + /** + * 删除关系关系网, 计算出差值, 可以临时放在 diffRenderer 中, 等待 commitUpdate 时统一处理 + */ private removeRelations(id: string): void { const relations = this.relationIndex.get(id) if (!relations) return - // Remove reverse relations - relations.center.forEach((relatedId) => this.relationIndex.get(relatedId)?.center.delete(id)) - relations.in.forEach((relatedId) => this.relationIndex.get(relatedId)?.out.delete(id)) - relations.out.forEach((relatedId) => this.relationIndex.get(relatedId)?.in.delete(id)) + const removeLine = (relatedId: string, type: LinkType) => { + const lineId = getLineId(id, relatedId, type) + this.lineDiffs.delete.set(lineId, { startId: id, endId: relatedId, type }) + } + + relations.center.forEach((relatedId) => { + this.relationIndex.get(relatedId)?.center.delete(id) + removeLine(relatedId, 'center') + }) + + relations.input.forEach((relatedId) => { + this.relationIndex.get(relatedId)?.output.delete(id) + removeLine(relatedId, 'in') + }) + + relations.output.forEach((relatedId) => { + this.relationIndex.get(relatedId)?.input.delete(id) + removeLine(relatedId, 'out') + }) this.relationIndex.delete(id) } + + private getDiffRenderer(type: string) { + let renderer = this.diffRenderer.get(type) + if (typeof renderer === 'undefined') { + renderer = getRenderer(type) + renderer.beginUpdate(this.viewport) + this.diffRenderer.set(type, renderer) + } + return renderer + } + + deleteEntityOnly(id: string) { + return this.entities.delete(id) + } + + findObjectsById(id: string) { + return this.objects.get(id) || [] + } + + deleteObjectsOnly(id: string) { + return this.objects.delete(id) + } + + appendObject(id: string, points: THREE.Object3D[]) { + this.objects.set(id, points) + } + + appendLineObject(id: string, lines: THREE.Object3D[]) { + this.lines.set(id, lines) + } + + findLineObjectsById(lineId: string): THREE.Object3D[] { + return this.lines.get(lineId) || [] + } + + deleteLineObjectOnly(id: string) { + return this.lines.delete(id) + } +} + +interface LineDiffItem { + startId: string + endId: string + type: LinkType +} + +export class Relation { + center = new Set() + input = new Set() + output = new Set() + + add(type: LinkType, id: string) { + if (type === 'in') + this.input.add(id) + else if (type === 'out') + this.output.add(id) + else if (type === 'center') + this.center.add(id) + else + throw new Error(`Unknown link type: ${type}`) + } + + delete(type: LinkType, id: string) { + if (type === 'in') + this.input.delete(id) + else if (type === 'out') + this.output.delete(id) + else if (type === 'center') + this.center.delete(id) + else + throw new Error(`Unknown link type: ${type}`) + } } \ No newline at end of file diff --git a/src/core/manager/StateManager.ts b/src/core/manager/StateManager.ts index e7044c3..24c0bde 100644 --- a/src/core/manager/StateManager.ts +++ b/src/core/manager/StateManager.ts @@ -1,7 +1,8 @@ import _ from 'lodash' import localforage from 'localforage' -import type Viewport from '@/core/engine/Viewport.ts' +import type EntityManager from './EntityManager' import { markRaw, reactive, ref } from 'vue' +import type Viewport from '@/core/engine/Viewport.ts' /** * 一种管理 地图数据状态的管理器, 他能够对数据进行增删改查,并且能够进行撤销、重做等操作 @@ -48,9 +49,9 @@ export default class StateManager { readonly id: string /** - * 视口对象, 用于获取、同步当前场景的状态 + * 实体对象, 用于同步当前场景的状态 */ - readonly viewport: Viewport + readonly entityManager: EntityManager /** * 是否发生了变化,通知外部是否需要保存数据 @@ -85,7 +86,7 @@ export default class StateManager { /** * 数据快照(用于差异计算) */ - private lastSnapshot: Map = new Map() + private lastSnapshot: Map = new Map() // 自动保存相关 private autoSaveInterval: number | null = null @@ -100,7 +101,7 @@ export default class StateManager { */ constructor(id: string, viewport: Viewport, bufferSize = 50) { this.id = id - this.viewport = viewport + this.entityManager = viewport.entityManager this.historyBufferSize = bufferSize // 初始化固定大小的历史缓冲区 @@ -193,11 +194,11 @@ export default class StateManager { /** * 将当前数据 与 viewport 进行同步, 对比出不同的部分,分别进行更新 - * - 调用 viewport.beginSync() 开始更新场景 - * - 调用 viewport.syncOnRemove(id) 删除场景中不存在的对象 - * - 调用 viewport.syncOnAppend(vdataItem) 添加场景中新的对象 - * - 调用 viewport.syncOnUpdate(id) 更新场景中已存在的对象 - * - 调用 viewport.endSync() 结束更新场景 + * - 调用 viewport.entityManager.beginUpdate() 开始更新 + * - 调用 viewport.entityManager.createEntity(vdataItem) 添加场景中新的实体 + * - 调用 viewport.entityManager.updateEntity(vdataItem) 新场景中已存在的实体 + * - 调用 viewport.entityManager.deleteEntity(id) 删除场景中的实体 + * - 调用 viewport.entityManager.commitUpdate() 结束更新场景 */ syncDataState() { // 没有变化时跳过同步 @@ -209,30 +210,30 @@ export default class StateManager { return } - this.viewport.beginSync() + this.entityManager.beginUpdate() // 处理删除 if (this.changeTracker.removed) { for (const id of this.changeTracker.removed) { - this.viewport.syncOnRemove(id) + this.entityManager.deleteEntity(id) } } // 处理新增 if (this.changeTracker.added) { for (const item of this.changeTracker.added) { - this.viewport.syncOnAppend(item) + this.entityManager.createEntity(item) } } // 处理更新 if (this.changeTracker.updated) { for (const item of this.changeTracker.updated) { - this.viewport.syncOnUpdate(item) + this.entityManager.updateEntity(item) } } - this.viewport.endSync() + this.entityManager.commitUpdate() } @@ -365,7 +366,7 @@ export default class StateManager { // 从历史快照恢复被删除的项目 const restoredItems = diff.removed .map(id => this.lastSnapshot.get(id)) - .filter(Boolean) as VDataItem[] + .filter(Boolean) as ItemJson[] this.vdata.items.push(...restoredItems) } @@ -381,7 +382,7 @@ export default class StateManager { const restoreMap = new Map( diff.updated .map(item => [item.id, this.lastSnapshot.get(item.id)]) - .filter(([, item]) => !!item) as [string, VDataItem][] + .filter(([, item]) => !!item) as [string, ItemJson][] ) this.vdata.items = this.vdata.items.map(item => @@ -458,11 +459,11 @@ export default class StateManager { } private fullSync() { - this.viewport.beginSync() + this.entityManager.beginUpdate() this.vdata.items.forEach(item => { - this.viewport.syncOnAppend(item) + this.entityManager.createEntity(item) }) - this.viewport.endSync() + this.entityManager.commitUpdate() } undoEnabled() { @@ -550,9 +551,9 @@ export default class StateManager { // 差异类型定义 interface DataDiff { - added: VDataItem[] + added: ItemJson[] removed: string[] - updated: VDataItem[] + updated: ItemJson[] } // 历史记录项 diff --git a/src/modules/measure/MeasureRenderer.ts b/src/modules/measure/MeasureRenderer.ts index 7bcf5d5..f780c37 100644 --- a/src/modules/measure/MeasureRenderer.ts +++ b/src/modules/measure/MeasureRenderer.ts @@ -1,40 +1,43 @@ -import type Viewport from '@/core/engine/Viewport.ts' import BaseRenderer from '@/core/base/BaseRenderer.ts' +import * as THREE from 'three' +import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry' +import { Line2 } from 'three/examples/jsm/lines/Line2' +import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial' /** * 辅助测量工具渲染器 */ export default class MeasureRenderer extends BaseRenderer { + static GROUP_NAME = 'measure-group' + static LABEL_NAME = 'measure_label' + static POINT_NAME = 'measure_point' + static LINE_NAME = 'measure_line' - // 开始更新, 可能暂停动画循环对本渲染器的动画等 - beginUpdate(viewport: Viewport) { - } + pointMaterial = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 }) - // 创建一个点 - createPoint(item: ItemJson, option?: RendererCudOption) { - } + lineMaterial = new LineMaterial({ + color: 0xE63C17, // 主颜色 + linewidth: 2, // 实际可用的线宽 + vertexColors: true, // 启用顶点颜色 + dashed: false, + alphaToCoverage: true + }) - // 删除一个点 - deletePoint(id, option?: RendererCudOption) { - } + 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 - // 更新一个点 - updatePoint(item: ItemJson, option?: RendererCudOption) { + return [obj] } - // 创建一根线 - createLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { - } + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { + const tt = new THREE.BoxGeometry(1, 1, 1) + const obj = new THREE.Mesh(tt, this.pointMaterial) + obj.name = MeasureRenderer.POINT_NAME - // 更新一根线 - updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { + return [obj] } - // 删除一根线 - deleteLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { - } - - // 结束更新 - endUpdate(viewport: Viewport) { - } } \ No newline at end of file diff --git a/src/types/Types.d.ts b/src/types/Types.d.ts index 086367f..a7959ec 100644 --- a/src/types/Types.d.ts +++ b/src/types/Types.d.ts @@ -15,7 +15,7 @@ interface VData { /** * 场景数据 */ - items: VDataItem[] + items: ItemJson[] /** * 是否发生了变化,通知外部是否需要保存数据 @@ -47,42 +47,3 @@ interface CatalogGroup { * 世界模型目录数据 */ type Catalog = CatalogGroup[]; - -interface VDataItem { - /** - * { - * id: 'p1', // 物体ID, 唯一标识, 需保证唯一, three.js 中的 uuid - * t: 'measure', // 物体类型, measure表示测量, 需交给 itemType.name == 'measure' 的组件处理 - * name: '', // 物体ID, 显示在 Tree 节点用 - * tf: [ // 变换矩阵, 3x3矩阵, 采用Y轴向上为正, X轴向右, Z轴向前的右手坐标系 - * [-9.0, 0, -1.0], // 平移向量 position - * [0, 0, 0], // 旋转向量 rotation, 表示绕Y轴旋转的角度, 单位为度。对应 three.js 应进行"角度"转"弧度"的换算 - * [0.25, 0.1, 0.25] // 缩放向量 scale - * ], - * dt: { // 用户数据, 可自定义, 一般用在 three.js 的 userData 中 - * label: '测量1', // 标签名称, 显示在 Tree/Map 用 - * color: '#ff0000', // 颜色, 显示用. 十六进制颜色值, three.js 中的材质颜色 - * center: ['p2'], // 用于 a='ln' 的测量线段, 关联的点对象(uuid) - * in: [], // 物流入方向关联的对象(uuid) - * out: [] // 物流出方向关联的对象(uuid) - * ... // 其他自定义数据 - * } - * } - */ - id: string - t: string - name: string - tf: [ - [number, number, number], - [number, number, number], - [number, number, number], - ] - dt: { - label: string - color: string - center?: string[] // 用于 a='ln' 的测量线段, 关联的点对象(uuid) - in?: string[] // 物流入方向关联的对象(uuid) - out?: string[] // 物流出方向关联的对象(uuid) - [key: string]: any // 其他自定义数据 - } -} \ No newline at end of file diff --git a/src/types/model.d.ts b/src/types/model.d.ts index 777087f..44a8b7c 100644 --- a/src/types/model.d.ts +++ b/src/types/model.d.ts @@ -19,7 +19,10 @@ interface RendererCudOption { * 实体操作选项 */ interface EntityCudOption { - // Additional options for create, update, delete operations + /** + * 原始实体对象 + */ + originEntity?: ItemJson } interface IGridHelper {