Browse Source

stateManager 测试用例1 通过;

stateManager.beginStateUpdate();
stateManager.vdata.items[1].tf[0] = [-10, 0, 4];
stateManager.endStateUpdate();
master
修宁 7 months ago
parent
commit
40d2eb4f8b
  1. 76
      src/core/base/BaseRenderer.ts
  2. 4
      src/core/engine/Viewport.ts
  3. 52
      src/core/manager/EntityManager.ts
  4. 58
      src/core/manager/StateManager.ts
  5. 4
      src/editor/Model2DEditor.vue
  6. 16
      src/modules/measure/MeasureRenderer.ts
  7. 12
      src/types/model.d.ts

76
src/core/base/BaseRenderer.ts

@ -18,7 +18,7 @@ export default abstract class BaseRenderer {
*
* @param viewport
*/
beginUpdate(viewport: Viewport): void {
beginRendererUpdate(viewport: Viewport): void {
this.tempViewport = viewport
}
@ -32,6 +32,46 @@ export default abstract class BaseRenderer {
*/
abstract createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D[]
fillObjectUserDataFromItem(item: ItemJson, ...objects: THREE.Object3D[]) {
_.forEach(objects, (object) => {
if (!object.name && item.name) {
object.name = item.name
}
object.userData = {
...object.userData,
createType: 'point',
entityId: item.id,
t: item.t
}
})
}
fillObjectUserDataFromLine(start: ItemJson, end: ItemJson, type: LinkType, ...objects: THREE.Object3D[]) {
const id = getLineId(start.id, end.id, type)
_.forEach(objects, (object) => {
if (!object.name) {
object.name = id
}
object.userData = {
...object.userData,
createType: 'line',
entityId: getLineId(start.id, end.id, type),
t: start.t
}
})
}
/**
*
*/
appendToScene(...objects: THREE.Object3D[]) {
if (!this.tempViewport || !this.tempViewport.scene) {
console.warn('No active viewport to append objects to.')
return
}
this.tempViewport.scene.add(...objects)
}
/**
*
* @param item
@ -44,15 +84,6 @@ export default abstract class BaseRenderer {
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])
@ -64,7 +95,9 @@ 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.tempViewport.entityManager.appendObject(item.id, points)
this.appendToScene(...points)
}
@ -97,15 +130,8 @@ export default abstract class BaseRenderer {
_.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.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2])
point.rotation.set(
THREE.MathUtils.degToRad(item.tf[1][0]),
@ -132,21 +158,15 @@ export default abstract class BaseRenderer {
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.fillObjectUserDataFromLine(start, end, type, ...lines)
this.tempViewport.entityManager.appendLineObject(id, lines)
this.appendToScene(...lines)
}
/**
@ -190,7 +210,7 @@ export default abstract class BaseRenderer {
/**
*
*/
endUpdate(): void {
endRendererUpdate(): void {
this.tempViewport = undefined
}
}

4
src/core/engine/Viewport.ts

@ -330,11 +330,11 @@ export default class Viewport {
}
}
beginUpdate() {
beginViewUpdate() {
this.state.isUpdating = true
}
endUpdate() {
endViewUpdate() {
this.state.isUpdating = false
}

52
src/core/manager/EntityManager.ts

@ -16,21 +16,21 @@ export default class EntityManager {
viewport: Viewport
// 所有数据点的实体
readonly entities = new Map<string, ItemJson>()
private readonly entities = new Map<string, ItemJson>()
// 关系索引
readonly relationIndex = new Map<string, Relation>()
private readonly relationIndex = new Map<string, Relation>()
// 所有 THREEJS "点"对象, 检索值是"点实体"的 id, 值是 THREE.Object3D 数组
readonly objects = new Map<string, THREE.Object3D[]>()
private readonly objects = new Map<string, THREE.Object3D[]>()
// 所有 THREEJS "线"对象, 检索值是"线实体"的 id, 取值方式是 {type}${startId}${endId}, 值是 THREE.Object3D 数组
readonly lines = new Map<string, THREE.Object3D[]>()
private readonly lines = new Map<string, THREE.Object3D[]>()
// 差量渲染器
readonly diffRenderer = new Map<string, BaseRenderer>()
private readonly diffRenderer = new Map<string, BaseRenderer>()
// 线差量记录
readonly lineDiffs = {
private readonly lineDiffs = {
create: new Map<string, LineDiffItem>(),
update: new Map<string, LineDiffItem>(),
delete: new Map<string, LineDiffItem>()
@ -44,9 +44,9 @@ export default class EntityManager {
/**
*
*/
beginUpdate(): void {
beginEntityUpdate(): void {
this.isUpdating = true
this.viewport.beginUpdate()
this.viewport.beginViewUpdate()
this.diffRenderer.clear()
this.lineDiffs.create.clear()
this.lineDiffs.update.clear()
@ -56,10 +56,11 @@ export default class EntityManager {
/**
* , center[] / in[] / out[] ,
*/
createOrUpdateEntity(entity: ItemJson, option: EntityCudOption = {}): void {
if (!entity?.id) {
createOrUpdateEntity(entityRaw: ItemJson, option: EntityCudOption = {}): void {
if (!entityRaw?.id) {
throw new Error('Entity must have an id')
}
const entity = _.cloneDeep(entityRaw) as ItemJson
// 找到这个数据的渲染器
const renderer = this.getDiffRenderer(entity.t)
@ -70,6 +71,29 @@ export default class EntityManager {
// 更新关系网
this.updateRelations(entity, originEntity)
// 判断坐标是否变化
if (originEntity?.tf && entity?.tf && !_.isEqual(originEntity.tf[0], entity.tf[0])) {
// 点的坐标发生变化, 要通知所有关联线更新
const relations = this.relationIndex.get(entity.id)
if (!relations) return
for (const type of (['center', 'in', 'out'] as LinkType[])) {
const relatedIds = relations[type]
if (!relatedIds) continue
for (const relatedId of relatedIds) {
const lineId = getLineId(entity.id, relatedId, type)
this.lineDiffs.update.set(lineId, { startId: entity.id, endId: relatedId, type })
// 如果是双向线(比如 center),也要反向加一次
if (type === 'center') {
this.lineDiffs.update.set(lineId, { startId: relatedId, endId: entity.id, type })
}
}
}
}
if (typeof originEntity === 'undefined') {
renderer.createPoint(entity, option)
@ -101,7 +125,7 @@ export default class EntityManager {
* - , /, UI上进行对应修改
* , , (BaseRenderer) createPoint / deletePoint / updatePoint / createLine / updateLine / deleteLine
*/
commitUpdate(): void {
endEntityUpdate(): void {
for (const [itemTypeName, renderer] of this.diffRenderer.entries()) {
for (const [lineId, lineDiffItem] of this.lineDiffs.create.entries()) {
const start = this.entities.get(lineDiffItem.startId)
@ -133,9 +157,9 @@ export default class EntityManager {
renderer.deleteLine(start, end, lineDiffItem.type)
}
renderer.endUpdate()
renderer.endRendererUpdate()
}
this.viewport.endUpdate()
this.viewport.endViewUpdate()
this.isUpdating = false
}
@ -311,7 +335,7 @@ export default class EntityManager {
let renderer = this.diffRenderer.get(type)
if (typeof renderer === 'undefined') {
renderer = getRenderer(type)
renderer.beginUpdate(this.viewport)
renderer.beginRendererUpdate(this.viewport)
this.diffRenderer.set(type, renderer)
}
return renderer

58
src/core/manager/StateManager.ts

@ -9,9 +9,9 @@ import type Viewport from '@/core/engine/Viewport.ts'
* 1. ,
* 2.
* 3. Interaction
* - 1. beginUserWrite
* - 1. beginStateUpdate
* - 2. vdata
* - 3. endUserWrite
* - 3. endStateUpdate
* 4. syncDataState() vdata viewport
* 5. syncDataState vdata viewport
* -
@ -19,28 +19,12 @@ import type Viewport from '@/core/engine/Viewport.ts'
*
*
*
* // 初始化
* const stateManager = new StateManager('scene-1', viewport)
*
* // 加载大数据
* await stateManager.load(largeDataSet) // 5万+ items
*
* // 修改数据
* stateManager.beginUserWrite()
*
* // 直接修改状态(实际项目应通过封装方法)
* stateManager.vdata.items.push(newItem)
* stateManager.vdata.items[0].name = 'updated'
* stateManager.vdata.items = stateManager.vdata.items.filter(i => i.id !== 'remove-id')
*
* stateManager.endUserWrite() // 自动计算差异并保存
* // 修改坐标点
* stateManager.beginStateUpdate();stateManager.vdata.items[1].tf[0] = [-10, 0, 4];stateManager.endStateUpdate();
*
* // 撤销操作
* stateManager.undo()
*
* // 保存到云端
* const data = await stateManager.save()
*
*/
export default class StateManager {
/**
@ -117,7 +101,7 @@ export default class StateManager {
/**
*
*/
beginUserWrite() {
beginStateUpdate() {
// 创建当前状态快照(非深拷贝)
this.lastSnapshot = new Map(
this.vdata.items.map(item => [item.id, _.cloneDeep(item)])
@ -128,7 +112,8 @@ export default class StateManager {
/**
*
*/
endUserWrite() {
endStateUpdate() {
debugger
this.calculateDiff()
this.saveStep()
this.syncDataState()
@ -197,11 +182,11 @@ export default class StateManager {
/**
* viewport
* - viewport.entityManager.beginUpdate()
* - viewport.entityManager.createEntity(vdataItem)
* - viewport.entityManager.updateEntity(vdataItem)
* - viewport.entityManager.deleteEntity(id)
* - viewport.entityManager.commitUpdate()
* - entityManager.beginEntityUpdate()
* - entityManager.createEntity(vdataItem)
* - entityManager.updateEntity(vdataItem)
* - entityManager.deleteEntity(id)
* - entityManager.endEntityUpdate()
*/
syncDataState() {
// 没有变化时跳过同步
@ -213,7 +198,7 @@ export default class StateManager {
return
}
this.entityManager.beginUpdate()
this.entityManager.beginEntityUpdate()
// 处理删除
if (this.changeTracker.removed) {
@ -236,7 +221,7 @@ export default class StateManager {
}
}
this.entityManager.commitUpdate()
this.entityManager.endEntityUpdate()
}
@ -262,8 +247,8 @@ export default class StateManager {
this.fullSync() // 同步到视口
// 初始状态作为第一步
this.beginUserWrite()
this.endUserWrite()
this.beginStateUpdate()
this.endStateUpdate()
this.isChanged.value = false
this.pendingChanges = false
@ -272,7 +257,7 @@ export default class StateManager {
await this.saveToLocalstore()
this.pendingChanges = false
console.log('[StateManager] 加载完成,共 ', data.items.length, '个对象')
console.log('[StateManager] 加载完成,共', data.items.length, '个对象')
} finally {
this.isLoading.value = false
@ -306,6 +291,7 @@ export default class StateManager {
*
*/
undo() {
debugger
if (!this.undoEnabled()) return
const step = this.historySteps[this.historyIndex]
@ -356,8 +342,6 @@ export default class StateManager {
updateMap.has(item.id) ? updateMap.get(item.id)! : item
)
}
this.lastSnapshot = new Map(this.vdata.items.map(item => [item.id, _.cloneDeep(item)]))
}
/**
@ -392,8 +376,6 @@ export default class StateManager {
restoreMap.has(item.id) ? restoreMap.get(item.id)! : item
)
}
this.lastSnapshot = new Map(this.vdata.items.map(item => [item.id, _.cloneDeep(item)]))
}
// /**
@ -462,11 +444,11 @@ export default class StateManager {
}
private fullSync() {
this.entityManager.beginUpdate()
this.entityManager.beginEntityUpdate()
this.vdata.items.forEach(item => {
this.entityManager.createOrUpdateEntity(item)
})
this.entityManager.commitUpdate()
this.entityManager.endEntityUpdate()
}
undoEnabled() {

4
src/editor/Model2DEditor.vue

@ -125,6 +125,8 @@ export default defineComponent({
delete window['editor']
delete window['viewport']
delete window['scene']
delete window['stateManager']
delete window['entityManager']
delete window['renderer']
delete window['camera']
delete window['renderer']
@ -157,6 +159,8 @@ export default defineComponent({
window['viewport'] = viewport
window['THREE'] = THREE
window['scene'] = sceneHelp.scene
window['stateManager'] = viewport.stateManager
window['entityManager'] = viewport.entityManager
window['renderer'] = viewport.renderer
window['camera'] = viewport.camera
window['renderer'] = viewport.renderer

16
src/modules/measure/MeasureRenderer.ts

@ -3,11 +3,17 @@ 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'
import { getLineId } from '@/core/ModelUtils.ts'
/**
*
*/
export default class MeasureRenderer extends BaseRenderer {
/**
* , 线. 线
*/
group: THREE.Group
static GROUP_NAME = 'measure-group'
static LABEL_NAME = 'measure_label'
static POINT_NAME = 'measure_point'
@ -28,6 +34,7 @@ export default class MeasureRenderer extends BaseRenderer {
const obj = new Line2(geom, this.lineMaterial)
obj.frustumCulled = false
obj.name = MeasureRenderer.LINE_NAME
obj.uuid = getLineId(start.id, end.id, type)
return [obj]
}
@ -36,8 +43,17 @@ export default class MeasureRenderer extends BaseRenderer {
const tt = new THREE.BoxGeometry(1, 1, 1)
const obj = new THREE.Mesh(tt, this.pointMaterial)
obj.name = MeasureRenderer.POINT_NAME
obj.uuid = item.id
return [obj]
}
appendToScene(...objects: THREE.Object3D[]) {
if (!this.group) {
this.group = new THREE.Group()
this.group.name = MeasureRenderer.GROUP_NAME
this.tempViewport?.scene.add(this.group)
}
this.group.add(...objects)
}
}

12
src/types/model.d.ts

@ -83,16 +83,16 @@ interface IGridHelper {
* ()
* :
* {
* id: 'p1', // 物体唯一ID, 也用于 three.js 中的 uuid
* id: 'p1', // 物体唯一ID
* t: 'measure', // 物体类型, measure表示测量, 需交给 itemType.name == 'measure' 的组件处理
* 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 中
* dt: { // 实体的自定义数据
* label: '测量1', // 标签名称, 显示用
* color: '#ff0000', // 颜色, 显示用. 十六进制颜色值, three.js 中的材质颜色
* color: '#ff0000', // 颜色, 显示用. 十六进制颜色值
* center: ['p2'], // S连线(又称逻辑连线), 与其他点之间的无方向性关联, 关系的起点需要在他的 dt.center[] 数组中添加目标点的id, 关系的终点需要在他的 dt.center[] 数组中添加起点的 id
* in: [], // A连线(又称物体流动线)的输入, 关系的终点需要在 dt.in[] 数组中添加起点的 id
* out: [] // A连线(又称物体流动线)的输出, 关系的起点需要在 dt.out[] 数组中添加目标点的 id
@ -144,12 +144,12 @@ interface ItemJson {
*/
dt: {
/**
* , , three.js userData.label , , , t的 renderer
* , , renderer
*/
label?: string
/**
* , three.js userData.color , , , t的 renderer
* , renderer
*/
color?: string
@ -157,10 +157,12 @@ interface ItemJson {
* S连线(线), , dt.center[] id, dt.center[] id
*/
center?: string[]
/**
* A连线(线), dt.in[] id
*/
in?: string[]
/**
* A连线(线), dt.out[] id
*/

Loading…
Cancel
Save