Browse Source

EntityManager 关系计算

master
修宁 7 months ago
parent
commit
bac6a64a0c
  1. 3
      doc/物流模型总体介绍.md
  2. 12
      src/core/ModelUtils.ts
  3. 160
      src/core/base/BaseRenderer.ts
  4. 10
      src/core/engine/SceneHelp.ts
  5. 72
      src/core/engine/Viewport.ts
  6. 326
      src/core/manager/EntityManager.ts
  7. 45
      src/core/manager/StateManager.ts
  8. 51
      src/modules/measure/MeasureRenderer.ts
  9. 41
      src/types/Types.d.ts
  10. 5
      src/types/model.d.ts

3
doc/物流模型总体介绍.md

@ -65,11 +65,8 @@
### 物流世界 ### 物流世界
一个物流仓库, 就是一个世界 一个物流仓库, 就是一个世界
他有自己的项目定义, 楼层, 围墙, 柱子, 其他数据, 个性化脚本等等 他有自己的项目定义, 楼层, 围墙, 柱子, 其他数据, 个性化脚本等等
每次对建模文件打开的时候, 因为性能问题, 一次只会读取一个楼层, 或者一个水平横截面. 每次对建模文件打开的时候, 因为性能问题, 一次只会读取一个楼层, 或者一个水平横截面.
因此, 一个 floor 就是对应一个 THREE.Scene 因此, 一个 floor 就是对应一个 THREE.Scene
```ts ```ts

12
src/core/ModelUtils.ts

@ -6,6 +6,18 @@ import { computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh'
import { Vector2 } from 'three/src/math/Vector2' import { Vector2 } from 'three/src/math/Vector2'
import type Toolbox from '@/model/itemType/Toolbox.ts' 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() { export function deletePointByKeyboard() {
system.msg('Delete not impleted yet') system.msg('Delete not impleted yet')
// const viewport: Viewport = window['viewport'] // const viewport: Viewport = window['viewport']

160
src/core/base/BaseRenderer.ts

@ -1,38 +1,121 @@
import type Viewport from '@/core/engine/Viewport' 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 * / 线 Three.js
*/ */
export default abstract class BaseRenderer { export default abstract class BaseRenderer {
/**
* beginUpdate viewport, endUpdate , BaseRenderer , viewport
*/
tempViewport?: Viewport = undefined
/** /**
* *
* @param viewport * @param viewport
*/ */
beginUpdate(viewport: Viewport): void { 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 item
* @param option * @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 id
* @param option * @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 item
* @param option * @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 type 线
* @param option * @param option
*/ */
abstract createLine( createLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
start: ItemJson, const lines = this.createLineBasic(start, end, type)
end: ItemJson, const id = getLineId(start.id, end.id, type)
type: 'in' | 'out' | 'center',
option?: RendererCudOption const startPoint = this.tempViewport.entityManager.findObjectsById(start.id)?.[0]
): void 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 type 线
* @param option * @param option
*/ */
abstract updateLine( updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
start: ItemJson, const lineId = getLineId(start.id, end.id, type)
end: ItemJson,
type: 'in' | 'out' | 'center', const lines = this.tempViewport.entityManager.findLineObjectsById(lineId)
option?: RendererCudOption _.forEach(lines, (line: THREE.Object3D) => {
): void 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 end
* @param option * @param option
*/ */
abstract deleteLine( deleteLine(start: ItemJson, end: ItemJson, option?: RendererCudOption) {
start: ItemJson, const id = getLineId(start.id, end.id, 'center')
end: ItemJson, const lines = this.tempViewport.entityManager.findLineObjectsById(id)
option?: RendererCudOption if (lines) {
): void this.tempViewport.scene.remove(...lines)
}
this.tempViewport.entityManager.deleteLineObjectOnly(id)
}
/** /**
* *
* @param viewport
*/ */
endUpdate(viewport: Viewport): void { endUpdate(): void {
// Optional: Resume animations or finalize batch updates this.tempViewport = undefined
} }
} }

10
src/core/engine/SceneHelp.ts

@ -78,8 +78,14 @@ export default class SceneHelp {
// }) // })
// } // }
remove(...object: THREE.Object3D[]) { remove(...objects: THREE.Object3D[]) {
this.scene.remove(...object) _.forEach(objects, (object) => {
if (object?.parent) {
object.parent.remove(object)
} else {
this.scene.remove(object)
}
})
} }
add(...object: THREE.Object3D[]) { add(...object: THREE.Object3D[]) {

72
src/core/engine/Viewport.ts

@ -21,8 +21,8 @@ import { calcPositionUseSnap } from '@/core/ModelUtils'
import StateManager from '@/core/manager/StateManager.ts' import StateManager from '@/core/manager/StateManager.ts'
/** /**
* *
* 使(使) * ,,,,,
*/ */
export default class Viewport { export default class Viewport {
viewerDom: HTMLElement viewerDom: HTMLElement
@ -49,41 +49,10 @@ export default class Viewport {
// 交互管理器 // 交互管理器
interactionManager = new InteractionManager() 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 resizeObserver?: ResizeObserver
/** // vue 的 watcher 管理器, 卸载时需要调用
* vue watcher
*/
watchList: (() => void)[] = [] watchList: (() => void)[] = []
css2DRenderer: CSS2DRenderer = new CSS2DRenderer() css2DRenderer: CSS2DRenderer = new CSS2DRenderer()
@ -92,6 +61,7 @@ export default class Viewport {
//@ts-ignore //@ts-ignore
state: ViewportState = reactive({ state: ViewportState = reactive({
isReady: false, isReady: false,
isUpdating: false,
cursorMode: 'normal', cursorMode: 'normal',
selectedObject: null, selectedObject: null,
camera: { camera: {
@ -275,7 +245,7 @@ export default class Viewport {
this.animationFrameId = requestAnimationFrame(this.animate.bind(this)) this.animationFrameId = requestAnimationFrame(this.animate.bind(this))
this.renderView() this.renderView()
if(window['lineMaterial']) { if (window['lineMaterial']) {
this.offset -= 0.002 this.offset -= 0.002
window['lineMaterial'].dashOffset = this.offset 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 { export interface ViewportState {
/** /**
*
*/
currentFloor: string
/**
* *
*/ */
isReady: boolean isReady: boolean
@ -463,6 +448,11 @@ export interface ViewportState {
selectedObjectMeta: ItemTypeMeta | null selectedObjectMeta: ItemTypeMeta | null
/** /**
*
*/
isUpdating: boolean
/**
* *
*/ */
camera: { camera: {

326
src/core/manager/EntityManager.ts

@ -1,37 +1,40 @@
import * as THREE from 'three' import * as THREE from 'three'
import type Viewport from '@/core/engine/Viewport'
import type BaseRenderer from '@/core/base/BaseRenderer'
import { getRenderer } from './ModuleManager' 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 { export default class EntityManager {
/**
* , , ThreeJs场景,,,
*/
viewport: Viewport viewport: Viewport
/** // 所有数据点的实体
* readonly entities = new Map<string, ItemJson>()
*/
entities = new Map<string, ItemJson>()
/** // 关系索引
* THREEJS readonly relationIndex = new Map<string, Relation>()
*/
objects = new Map<string, THREE.Object3D[]>()
/** // 所有 THREEJS "点"对象
* readonly objects = new Map<string, THREE.Object3D[]>()
*/
relationIndex = new Map<string, { center: Set<string>; in: Set<string>; out: Set<string> }>()
/** // 所有 THREEJS "线"对象
* THREEJS readonly lines = new Map<string, THREE.Object3D[]>()
*/
lines = new Map<string, THREE.Object3D[]>()
private batchMode = false // 差量渲染器
readonly diffRenderer = new Map<string, BaseRenderer>()
// 线差量记录
lineDiffs = {
create: new Map<string, LineDiffItem>(),
update: new Map<string, LineDiffItem>(),
delete: new Map<string, LineDiffItem>()
}
init(viewport: Viewport) { init(viewport: Viewport) {
this.viewport = viewport this.viewport = viewport
@ -41,48 +44,51 @@ export default class EntityManager {
* *
*/ */
beginUpdate(): void { beginUpdate(): void {
this.batchMode = true this.viewport.beginUpdate()
this.viewport.beginSync() 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 { createOrUpdateEntity(entity: ItemJson, option: EntityCudOption = {}): void {
if (this.entities.has(entity.id!)) { if (!entity?.id) {
throw new Error(`Entity with ID "${entity.id}" already exists.`) throw new Error('Entity must have an id')
} }
this.entities.set(entity.id!, entity)
this.updateRelations(entity) // 找到这个数据的渲染器
const renderer = getRenderer(entity.t) 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) renderer.createPoint(entity, option)
}
/** } else {
* , , , dt.center[] / dt.in[] / dt.out[] option.originEntity = _.cloneDeep(originEntity)
*/
updateEntity(entity: ItemJson, option?: EntityCudOption): void {
if (!this.entities.has(entity.id!)) {
throw new Error(`Entity with ID "${entity.id}" does not exist.`)
}
this.entities.set(entity.id!, entity)
this.updateRelations(entity)
const renderer = getRenderer(entity.t)
renderer.updatePoint(entity, option) renderer.updatePoint(entity, option)
} }
}
/** /**
* , 线, , center[] / in[] / out[] * , 线, , center[] / in[] / out[]
*/ */
deleteEntity(id: string, option?: EntityCudOption): void { deleteEntity(id: string, option: EntityCudOption = {}): void {
const entity = this.entities.get(id) const entity = this.entities.get(id)
if (!entity) { if (!entity) return
throw new Error(`Entity with ID "${id}" does not exist.`)
} option.originEntity = _.cloneDeep(entity)
this.entities.delete(id) this.entities.delete(id)
this.removeRelations(id) this.removeRelations(id)
const renderer = getRenderer(entity.t) this.getDiffRenderer(entity.t).deletePoint(id, option)
renderer.deletePoint(id, option)
} }
/** /**
@ -92,66 +98,216 @@ export default class EntityManager {
* - , 线, , center[] / in[] / out[] * - , 线, , center[] / in[] / out[]
* - , /, UI上进行对应修改 * - , /, UI上进行对应修改
* , , (BaseRenderer) createPoint / deletePoint / updatePoint / createLine / updateLine / deleteLine * , , (BaseRenderer) createPoint / deletePoint / updatePoint / createLine / updateLine / deleteLine
* viewport.getItemTypeRenderer(itemTypeName)
*/ */
commitUpdate(): void { commitUpdate(): void {
this.batchMode = false for (const [itemTypeName, renderer] of this.diffRenderer.entries()) {
this.viewport.endSync() 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)
} }
/** for (const [lineId, lineDiffItem] of this.lineDiffs.update.entries()) {
* const start = this.entities.get(lineDiffItem.startId)
*/ const end = this.entities.get(lineDiffItem.endId)
getEntity(id: string): ItemJson | undefined { if (start.t !== itemTypeName) {
return this.entities.get(id) // 只通知起点对应的渲染器
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)
getRelatedEntities(id: string, relationType: 'center' | 'in' | 'out'): ItemJson[] { if (start.t !== itemTypeName) {
const relations = this.relationIndex.get(id)?.[relationType] || new Set() // 只通知起点对应的渲染器
return Array.from(relations).map((relatedId) => this.entities.get(relatedId)!) continue
}
renderer.deleteLine(start, end, lineDiffItem.type)
}
renderer.endUpdate()
}
this.viewport.endUpdate()
} }
private updateRelations(entity: ItemJson): void { /**
* , , diffRenderer , commitUpdate
*/
private updateRelations(entity: ItemJson, originEntity?: ItemJson): void {
const { id, dt } = entity const { id, dt } = entity
if (!id || !dt) return if (!id || !dt) return
const relations = this.relationIndex.get(id) || { center: new Set(), in: new Set(), out: new Set() } const oldCenter = new Set(originEntity?.dt?.center || [])
relations.center = new Set(dt.center || []) const oldIn = new Set(originEntity?.dt?.in || [])
relations.in = new Set(dt.in || []) const oldOut = new Set(originEntity?.dt?.out || [])
relations.out = new Set(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) this.relationIndex.set(id, relations)
// Update reverse relations // 更新反向关系
this.updateReverseRelations(id, dt.center, 'center') this.updateReverseRelations(id, oldCenter, newCenter, 'center')
this.updateReverseRelations(id, dt.in, 'out') this.updateReverseRelations(id, oldIn, newIn, 'out') // 入边的反向是出边
this.updateReverseRelations(id, dt.out, 'in') 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 { private updateReverseRelations(id: string, oldIds: Set<string>, newIds: Set<string>, relationType: LinkType) {
if (!relatedIds) return // 移除旧关系
relatedIds.forEach((relatedId) => { for (const relatedId of oldIds) {
const relatedRelations = this.relationIndex.get(relatedId) || { if (!newIds.has(relatedId)) {
center: new Set(), const rev = this.relationIndex.get(relatedId)
in: new Set(), rev.delete(relationType, id)
out: new Set()
} }
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<string>, newIds: Set<string>, 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 { private removeRelations(id: string): void {
const relations = this.relationIndex.get(id) const relations = this.relationIndex.get(id)
if (!relations) return if (!relations) return
// Remove reverse relations const removeLine = (relatedId: string, type: LinkType) => {
relations.center.forEach((relatedId) => this.relationIndex.get(relatedId)?.center.delete(id)) const lineId = getLineId(id, relatedId, type)
relations.in.forEach((relatedId) => this.relationIndex.get(relatedId)?.out.delete(id)) this.lineDiffs.delete.set(lineId, { startId: id, endId: relatedId, type })
relations.out.forEach((relatedId) => this.relationIndex.get(relatedId)?.in.delete(id)) }
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) 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<string>()
input = new Set<string>()
output = new Set<string>()
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}`)
}
} }

45
src/core/manager/StateManager.ts

@ -1,7 +1,8 @@
import _ from 'lodash' import _ from 'lodash'
import localforage from 'localforage' import localforage from 'localforage'
import type Viewport from '@/core/engine/Viewport.ts' import type EntityManager from './EntityManager'
import { markRaw, reactive, ref } from 'vue' 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 id: string
/** /**
* , * ,
*/ */
readonly viewport: Viewport readonly entityManager: EntityManager
/** /**
* *
@ -85,7 +86,7 @@ export default class StateManager {
/** /**
* *
*/ */
private lastSnapshot: Map<string, VDataItem> = new Map() private lastSnapshot: Map<string, ItemJson> = new Map()
// 自动保存相关 // 自动保存相关
private autoSaveInterval: number | null = null private autoSaveInterval: number | null = null
@ -100,7 +101,7 @@ export default class StateManager {
*/ */
constructor(id: string, viewport: Viewport, bufferSize = 50) { constructor(id: string, viewport: Viewport, bufferSize = 50) {
this.id = id this.id = id
this.viewport = viewport this.entityManager = viewport.entityManager
this.historyBufferSize = bufferSize this.historyBufferSize = bufferSize
// 初始化固定大小的历史缓冲区 // 初始化固定大小的历史缓冲区
@ -193,11 +194,11 @@ export default class StateManager {
/** /**
* viewport * viewport
* - viewport.beginSync() * - viewport.entityManager.beginUpdate()
* - viewport.syncOnRemove(id) * - viewport.entityManager.createEntity(vdataItem)
* - viewport.syncOnAppend(vdataItem) * - viewport.entityManager.updateEntity(vdataItem)
* - viewport.syncOnUpdate(id) * - viewport.entityManager.deleteEntity(id)
* - viewport.endSync() * - viewport.entityManager.commitUpdate()
*/ */
syncDataState() { syncDataState() {
// 没有变化时跳过同步 // 没有变化时跳过同步
@ -209,30 +210,30 @@ export default class StateManager {
return return
} }
this.viewport.beginSync() this.entityManager.beginUpdate()
// 处理删除 // 处理删除
if (this.changeTracker.removed) { if (this.changeTracker.removed) {
for (const id of this.changeTracker.removed) { for (const id of this.changeTracker.removed) {
this.viewport.syncOnRemove(id) this.entityManager.deleteEntity(id)
} }
} }
// 处理新增 // 处理新增
if (this.changeTracker.added) { if (this.changeTracker.added) {
for (const item of this.changeTracker.added) { for (const item of this.changeTracker.added) {
this.viewport.syncOnAppend(item) this.entityManager.createEntity(item)
} }
} }
// 处理更新 // 处理更新
if (this.changeTracker.updated) { if (this.changeTracker.updated) {
for (const item of 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 const restoredItems = diff.removed
.map(id => this.lastSnapshot.get(id)) .map(id => this.lastSnapshot.get(id))
.filter(Boolean) as VDataItem[] .filter(Boolean) as ItemJson[]
this.vdata.items.push(...restoredItems) this.vdata.items.push(...restoredItems)
} }
@ -381,7 +382,7 @@ export default class StateManager {
const restoreMap = new Map( const restoreMap = new Map(
diff.updated diff.updated
.map(item => [item.id, this.lastSnapshot.get(item.id)]) .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 => this.vdata.items = this.vdata.items.map(item =>
@ -458,11 +459,11 @@ export default class StateManager {
} }
private fullSync() { private fullSync() {
this.viewport.beginSync() this.entityManager.beginUpdate()
this.vdata.items.forEach(item => { this.vdata.items.forEach(item => {
this.viewport.syncOnAppend(item) this.entityManager.createEntity(item)
}) })
this.viewport.endSync() this.entityManager.commitUpdate()
} }
undoEnabled() { undoEnabled() {
@ -550,9 +551,9 @@ export default class StateManager {
// 差异类型定义 // 差异类型定义
interface DataDiff { interface DataDiff {
added: VDataItem[] added: ItemJson[]
removed: string[] removed: string[]
updated: VDataItem[] updated: ItemJson[]
} }
// 历史记录项 // 历史记录项

51
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 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 { 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'
// 开始更新, 可能暂停动画循环对本渲染器的动画等 pointMaterial = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 })
beginUpdate(viewport: Viewport) {
}
// 创建一个点 lineMaterial = new LineMaterial({
createPoint(item: ItemJson, option?: RendererCudOption) { color: 0xE63C17, // 主颜色
} linewidth: 2, // 实际可用的线宽
vertexColors: true, // 启用顶点颜色
dashed: false,
alphaToCoverage: true
})
// 删除一个点 createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D[] {
deletePoint(id, option?: RendererCudOption) { const geom = new LineGeometry()
} const obj = new Line2(geom, this.lineMaterial)
obj.frustumCulled = false
obj.name = MeasureRenderer.LINE_NAME
// 更新一个点 return [obj]
updatePoint(item: ItemJson, option?: RendererCudOption) {
} }
// 创建一根线 createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] {
createLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { const tt = new THREE.BoxGeometry(1, 1, 1)
} const obj = new THREE.Mesh(tt, this.pointMaterial)
obj.name = MeasureRenderer.POINT_NAME
// 更新一根线 return [obj]
updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
} }
// 删除一根线
deleteLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
}
// 结束更新
endUpdate(viewport: Viewport) {
}
} }

41
src/types/Types.d.ts

@ -15,7 +15,7 @@ interface VData {
/** /**
* *
*/ */
items: VDataItem[] items: ItemJson[]
/** /**
* *
@ -47,42 +47,3 @@ interface CatalogGroup {
* *
*/ */
type Catalog = 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 // 其他自定义数据
}
}

5
src/types/model.d.ts

@ -19,7 +19,10 @@ interface RendererCudOption {
* *
*/ */
interface EntityCudOption { interface EntityCudOption {
// Additional options for create, update, delete operations /**
*
*/
originEntity?: ItemJson
} }
interface IGridHelper { interface IGridHelper {

Loading…
Cancel
Save