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
```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 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']

160
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
}
}

10
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[]) {

72
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: {

326
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<string, ItemJson>()
// 所有数据点的实体
readonly entities = new Map<string, ItemJson>()
/**
* THREEJS
*/
objects = new Map<string, THREE.Object3D[]>()
// 关系索引
readonly relationIndex = new Map<string, Relation>()
/**
*
*/
relationIndex = new Map<string, { center: Set<string>; in: Set<string>; out: Set<string> }>()
// 所有 THREEJS "点"对象
readonly objects = new Map<string, THREE.Object3D[]>()
/**
* THREEJS
*/
lines = new Map<string, THREE.Object3D[]>()
// 所有 THREEJS "线"对象
readonly 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) {
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)
// 找到这个数据的渲染器
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)
}
/**
* , , , 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.`)
}
this.entities.set(entity.id!, entity)
this.updateRelations(entity)
const renderer = getRenderer(entity.t)
} else {
option.originEntity = _.cloneDeep(originEntity)
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)
}
/**
*
*/
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)!)
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()
}
private updateRelations(entity: ItemJson): void {
/**
* , , diffRenderer , commitUpdate
*/
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<string>, newIds: Set<string>, 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<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 {
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<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 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<string, VDataItem> = new Map()
private lastSnapshot: Map<string, ItemJson> = 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[]
}
// 历史记录项

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 * 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) {
}
}

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[];
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 {
// Additional options for create, update, delete operations
/**
*
*/
originEntity?: ItemJson
}
interface IGridHelper {

Loading…
Cancel
Save