Browse Source

mousemove

master
修宁 7 months ago
parent
commit
19a302d5ff
  1. 5
      src/core/base/BaseRenderer.ts
  2. 4
      src/core/controls/SelectInspect.ts
  3. 145
      src/core/manager/EntityManager.ts
  4. 5
      src/editor/widgets/property/PropertyView.vue
  5. 2
      src/modules/gstore/GstoreRenderer.ts
  6. 4
      src/modules/measure/MeasureRenderer.ts
  7. 2
      src/modules/rack/RackRenderer.ts
  8. 6
      src/modules/way/WayRenderer.ts
  9. 75
      src/utils/webutils.ts

5
src/core/base/BaseRenderer.ts

@ -88,6 +88,7 @@ export default abstract class BaseRenderer {
createType: 'point', createType: 'point',
entityId: item.id, entityId: item.id,
draggable: item.dt.protected !== true, draggable: item.dt.protected !== true,
selectable: item.dt.selectable !== false,
t: item.t t: item.t
} }
} }
@ -103,6 +104,10 @@ export default abstract class BaseRenderer {
...object.userData, ...object.userData,
createType: 'line', createType: 'line',
entityId: getLineId(start.id, end.id, type), entityId: getLineId(start.id, end.id, type),
startId: start.id,
endId: end.id,
draggable: false,
selectable: false,
t: start.t t: start.t
} }
} }

4
src/core/controls/SelectInspect.ts

@ -312,11 +312,11 @@ export default class SelectInspect implements IControls {
if (Date.now() - clickTime < 200) { if (Date.now() - clickTime < 200) {
// 如果是点击事件,触发选中逻辑 // 如果是点击事件,触发选中逻辑
const objects: THREE.Object3D[] = this.viewport.entityManager.getObjectByCanvasMouse(event) const objects: THREE.Object3D[] = this.viewport.entityManager.getObjectByCanvasMouse(event)
if (objects.length > 0 && objects[0]?.userData?.entityId) { if (objects.length > 0 && objects[0]?.userData?.entityId && objects[0]?.userData?.createType !== 'line') {
console.log('mouseClick', objects) console.log('mouseClick', objects)
const object = objects[0] const object = objects[0]
const entityId = object.userData.entityId const entityId = object.userData.entityId
const item = this.viewport.entityManager.findItemById(entityId) let item = this.viewport.entityManager.findItemById(entityId)
const itemTypeName = object.userData.t const itemTypeName = object.userData.t
if (item.dt.protected !== true) { if (item.dt.protected !== true) {
this.viewport.state.selectedObject = markRaw(object) this.viewport.state.selectedObject = markRaw(object)

145
src/core/manager/EntityManager.ts

@ -4,6 +4,7 @@ import type BaseRenderer from '@/core/base/BaseRenderer'
import { getRenderer } from './ModuleManager' import { getRenderer } from './ModuleManager'
import { getClosestObject, getLineId, parseLineId } from '@/core/ModelUtils' import { getClosestObject, getLineId, parseLineId } from '@/core/ModelUtils'
import { Vector2 } from 'three' import { Vector2 } from 'three'
import { getFreezeDeep } from '@/utils/webutils.ts'
/** /**
* *
@ -17,7 +18,7 @@ export default class EntityManager {
viewport: Viewport viewport: Viewport
// 所有数据点的实体 // 所有数据点的实体
private readonly entities = new Map<string, ItemJson>() private readonly ___entityMap = new Map<string, ItemJson>()
// 关系索引 // 关系索引
private readonly relationIndex = new Map<string, Relation>() private readonly relationIndex = new Map<string, Relation>()
@ -26,7 +27,9 @@ export default class EntityManager {
private readonly objects = new Map<string, THREE.Object3D[]>() private readonly objects = new Map<string, THREE.Object3D[]>()
// 所有 THREEJS "可选中"对象, 检索值是"点实体"的 id, 值是 THREE.Object3D 数组 // 所有 THREEJS "可选中"对象, 检索值是"点实体"的 id, 值是 THREE.Object3D 数组
private readonly _selectableObjects: THREE.Object3D[] = [] readonly _selectableObjects: THREE.Object3D[] = []
readonly _draggableObjects: THREE.Object3D[] = []
// 所有 THREEJS "线"对象, 检索值是"线实体"的 id, 取值方式是 {type}${startId}${endId}, 值是 THREE.Object3D 数组 // 所有 THREEJS "线"对象, 检索值是"线实体"的 id, 取值方式是 {type}${startId}${endId}, 值是 THREE.Object3D 数组
private readonly lines = new Map<string, THREE.Object3D[]>() private readonly lines = new Map<string, THREE.Object3D[]>()
@ -51,7 +54,7 @@ export default class EntityManager {
this.diffRenderer.clear() this.diffRenderer.clear()
// 清理所有实体和关系索引 // 清理所有实体和关系索引
this.entities.clear() this.___entityMap.clear()
this.relationIndex.clear() this.relationIndex.clear()
this.objects.clear() this.objects.clear()
this.lines.clear() this.lines.clear()
@ -62,14 +65,14 @@ export default class EntityManager {
* *
*/ */
cloneWriteBackEntities(): Map<string, ItemJson> { cloneWriteBackEntities(): Map<string, ItemJson> {
const entities = new Map<string, ItemJson>() const result = new Map<string, ItemJson>()
for (const id of this.writeBackEntities) { for (const id of this.writeBackEntities) {
const entity = this.entities.get(id) const entity = this.___entityMap.get(id)
if (!entity) continue if (!entity) continue
entities.set(id, _.cloneDeep(entity)) result.set(id, _.cloneDeep(entity))
} }
console.log('需要回写', entities.size, '行数据') console.log('需要回写', result.size, '行数据')
return entities return result
} }
init(viewport: Viewport) { init(viewport: Viewport) {
@ -97,14 +100,14 @@ export default class EntityManager {
throw new Error('Entity must have an id') throw new Error('Entity must have an id')
} }
const entity = _.cloneDeep(entityRaw) as ItemJson const entity = _.cloneDeep(entityRaw) as ItemJson
const originEntity = this.entities.get(entity.id) const originEntity = this.___entityMap.get(entity.id)
// 找到这个数据的渲染器 // 找到这个数据的渲染器
const renderer = this.getDiffRenderer(entity.t) const renderer = this.getDiffRenderer(entity.t)
// 先判断坐标是否变化 // 先判断坐标是否变化
const coordinateChanged = originEntity?.tf && entity?.tf && !_.isEqual(originEntity.tf[0], entity.tf[0]) const coordinateChanged = originEntity?.tf && entity?.tf && !_.isEqual(originEntity.tf[0], entity.tf[0])
this.entities.set(entity.id, entity) this.___entityMap.set(entity.id, entity)
// 更新关系网 // 更新关系网
this.updateRelations(entity, originEntity) this.updateRelations(entity, originEntity)
@ -148,7 +151,7 @@ export default class EntityManager {
* , 线, , 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.___entityMap.get(id)
if (!entity) return if (!entity) return
option.originEntity = _.cloneDeep(entity) option.originEntity = _.cloneDeep(entity)
@ -157,7 +160,7 @@ export default class EntityManager {
// 先生成线差量,再清理关系 // 先生成线差量,再清理关系
this.generateLineDiffsForDelete(id) this.generateLineDiffsForDelete(id)
this.removeRelations(id) // 清理关系 this.removeRelations(id) // 清理关系
this.entities.delete(id) // 删除实体 this.___entityMap.delete(id) // 删除实体
this.getDiffRenderer(entity.t).deletePoint(id, option as RendererCudOption) this.getDiffRenderer(entity.t).deletePoint(id, option as RendererCudOption)
} }
@ -189,8 +192,8 @@ export default class EntityManager {
for (const [itemTypeName, renderer] of this.diffRenderer.entries()) { for (const [itemTypeName, renderer] of this.diffRenderer.entries()) {
// "线"创建 // "线"创建
for (const [lineId, lineDiffItem] of this.lineDiffs.create.entries()) { for (const [lineId, lineDiffItem] of this.lineDiffs.create.entries()) {
const start = this.entities.get(lineDiffItem.startId) const start = this.___entityMap.get(lineDiffItem.startId)
const end = this.entities.get(lineDiffItem.endId) const end = this.___entityMap.get(lineDiffItem.endId)
// 添加存在性检查 // 添加存在性检查
if (!start || !end) { if (!start || !end) {
@ -207,8 +210,8 @@ export default class EntityManager {
// "线"更新 // "线"更新
for (const [lineId, lineDiffItem] of this.lineDiffs.update.entries()) { for (const [lineId, lineDiffItem] of this.lineDiffs.update.entries()) {
const start = this.entities.get(lineDiffItem.startId) const start = this.___entityMap.get(lineDiffItem.startId)
const end = this.entities.get(lineDiffItem.endId) const end = this.___entityMap.get(lineDiffItem.endId)
// 添加存在性检查 // 添加存在性检查
if (!start || !end) { if (!start || !end) {
@ -226,8 +229,8 @@ export default class EntityManager {
// "线"删除 // "线"删除
for (const [lineId, lineDiffItem] of this.lineDiffs.delete.entries()) { for (const [lineId, lineDiffItem] of this.lineDiffs.delete.entries()) {
const start = this.entities.get(lineDiffItem.startId) const start = this.___entityMap.get(lineDiffItem.startId)
const end = this.entities.get(lineDiffItem.endId) const end = this.___entityMap.get(lineDiffItem.endId)
if (!start || !end) { if (!start || !end) {
// 即使实体不存在也要处理删除 // 即使实体不存在也要处理删除
@ -265,7 +268,7 @@ export default class EntityManager {
needUpdateIds.add(lineDiffItem.endId) needUpdateIds.add(lineDiffItem.endId)
} }
for (const id of needUpdateIds) { for (const id of needUpdateIds) {
const entity = this.entities.get(id) const entity = this.___entityMap.get(id)
if (entity) { if (entity) {
entity.dt.center = Array.from(this.relationIndex.get(id)?.center || []) entity.dt.center = Array.from(this.relationIndex.get(id)?.center || [])
entity.dt.in = Array.from(this.relationIndex.get(id)?.input || []) entity.dt.in = Array.from(this.relationIndex.get(id)?.input || [])
@ -407,115 +410,45 @@ export default class EntityManager {
return renderer return renderer
} }
// /**
// * 重命名一个点
// * 注意, 不能在更新时刻改名. 所有的关系节点都应该改名
// */
// renamePoint(newId: string, originId: string) {
// if (this.isUpdating) {
// throw new Error('Cannot rename point during update')
// }
// const entity = this.entities.get(originId)
// if (!entity) {
// throw new Error(`Entity with id ${originId} does not exist`)
// }
// if (this.entities.has(newId)) {
// throw new Error(`Entity with id ${newId} already exists`)
// }
// entity.id = newId
// this.entities.set(newId, entity)
// this.entities.delete(originId)
// this.objects.set(newId, this.objects.get(originId) || [])
// this.objects.delete(originId)
//
// // 更新关系索引
// const relations = this.relationIndex.get(originId)
// if (relations) {
// this.relationIndex.delete(originId)
//
// // 更新所有关系中的 id
// relations.center.forEach((relatedId) => {
// const rev = this.relationIndex.get(relatedId)
// if (rev && rev.delete('center', originId)) {
// rev.add('center', newId)
// }
// })
// relations.input.forEach((relatedId) => {
// const rev = this.relationIndex.get(relatedId)
// if (rev && rev.delete('out', originId)) {
// rev.add('out', newId)
// }
// })
// relations.output.forEach((relatedId) => {
// const rev = this.relationIndex.get(relatedId)
// if (rev && rev.delete('in', originId)) {
// rev.add('in', newId)
// }
// })
//
// this.relationIndex.set(newId, relations)
// }
//
// // 更新所有线段数据
// for (const [lineId, lineObjects] of this.lines.entries()) {
// const [type, startId, endId] = parseLineId(lineId)
// if (startId === originId) {
// const newLineId = getLineId(newId, endId, type)
// this.lines.set(newLineId, lineObjects)
// this.lines.delete(lineId)
//
// } else if (endId === originId) {
// const newLineId = getLineId(startId, newId, type)
// this.lines.set(newLineId, lineObjects)
// this.lines.delete(lineId)
// }
// }
// }
deleteEntityOnly(id: string) { deleteEntityOnly(id: string) {
return this.entities.delete(id) return this.___entityMap.delete(id)
} }
findObjectsById(id: string) { findObjectsById(id: string) {
return this.objects.get(id) || [] return this.objects.get(id) || []
} }
deleteObjectsOnly(id: string) {
// 删除对象时,也需要从 _selectableObjects 中移除
const rel = this.objects.get(id)
if (rel) {
_.remove(this._selectableObjects, obj => !rel.includes(obj))
}
return this.objects.delete(id)
}
appendObject(id: string, points: THREE.Object3D[]) { appendObject(id: string, points: THREE.Object3D[]) {
this.objects.set(id, points) this.objects.set(id, points)
// 如果是可选中对象,添加到 _selectableObjects 中 // 如果是可选中对象,添加到 _selectableObjects 中
if (points.some(obj => obj.userData.selectable !== false)) { for (const point of points) {
this._selectableObjects.push(...points) if (point.userData.selectable !== false) {
this._selectableObjects.push(point)
}
if (point.userData.draggable) {
this._draggableObjects.push(point)
}
} }
} }
appendLineObject(id: string, lines: THREE.Object3D[]) { appendLineObject(id: string, lines: THREE.Object3D[]) {
this.lines.set(id, lines) this.lines.set(id, lines)
// 如果是可选中对象,添加到 _selectableObjects 中
if (lines.some(obj => obj.userData.selectable !== false)) {
this._selectableObjects.push(...lines)
}
} }
findLineObjectsById(lineId: string): THREE.Object3D[] { findLineObjectsById(lineId: string): THREE.Object3D[] {
return this.lines.get(lineId) || [] return this.lines.get(lineId) || []
} }
deleteObjectsOnly(id: string) {
_.remove(this._selectableObjects, obj => obj.userData?.entityId === id)
_.remove(this._draggableObjects, obj => obj.userData?.entityId === id)
return this.objects.delete(id)
}
deleteLineObjectOnly(id: string) { deleteLineObjectOnly(id: string) {
// 删除线对象时,也需要从 _selectableObjects 中移除 // 删除线对象时,也需要从 _selectableObjects 中移除
const rel = this.lines.get(id) _.remove(this._selectableObjects, obj => obj.userData?.entityId === id)
if (rel) { _.remove(this._draggableObjects, obj => obj.userData?.entityId === id)
_.remove(this._selectableObjects, obj => !rel.includes(obj))
}
return this.lines.delete(id) return this.lines.delete(id)
} }
@ -526,7 +459,7 @@ export default class EntityManager {
if (!linkStartPointId) { if (!linkStartPointId) {
return return
} }
return this.entities.get(linkStartPointId) return getFreezeDeep(this.___entityMap.get(linkStartPointId))
} }
getObjectByCanvasMouse(event: MouseEvent): THREE.Object3D[] { getObjectByCanvasMouse(event: MouseEvent): THREE.Object3D[] {

5
src/editor/widgets/property/PropertyView.vue

@ -1,7 +1,8 @@
<template> <template>
<div class="title"> <div class="title">
<template v-if="!!t"> <template v-if="!!t">
属性 <el-tag type="primary">{{ t }}</el-tag> 属性
<el-tag type="primary">{{ t }}</el-tag>
<el-input v-model="searchKeyword" size="small" style="width: 240px" placeholder="Search"> <el-input v-model="searchKeyword" size="small" style="width: 240px" placeholder="Search">
<template #prefix> <template #prefix>
<component :is="renderIcon('element Search')"></component> <component :is="renderIcon('element Search')"></component>
@ -85,7 +86,7 @@ export default {
}, },
computed: { computed: {
t() { t() {
return this.selectedItem ? ('(' + this.selectedItem.t + ')') : '' return this.selectedItem ? this.selectedItem.t : ''
}, },
selectedItem() { selectedItem() {
return this.state?.selectedItem return this.state?.selectedItem

2
src/modules/gstore/GstoreRenderer.ts

@ -101,6 +101,6 @@ export default class GstoreRenderer extends BaseRenderer {
dispose() { dispose() {
super.dispose() super.dispose()
this.pointMaterial.dispose() this.pointMaterial?.dispose()
} }
} }

4
src/modules/measure/MeasureRenderer.ts

@ -209,7 +209,7 @@ export default class MeasureRenderer extends BaseRenderer {
} }
this.group = undefined this.group = undefined
this.pointMaterial.dispose() this.pointMaterial?.dispose()
this.lineMaterial.dispose() this.lineMaterial?.dispose()
} }
} }

2
src/modules/rack/RackRenderer.ts

@ -137,7 +137,7 @@ export default class RackRenderer extends BaseRenderer {
dispose() { dispose() {
super.dispose() super.dispose()
this.pointMaterial.dispose() this.pointMaterial?.dispose()
} }
createPointBasic(item: ItemJson, option?: RendererCudOption): Object3D[] { createPointBasic(item: ItemJson, option?: RendererCudOption): Object3D[] {

6
src/modules/way/WayRenderer.ts

@ -16,8 +16,6 @@ export default class WayRenderer extends BaseRenderer {
static POINT_NAME = 'way_point' static POINT_NAME = 'way_point'
static LINE_NAME = 'way_line' static LINE_NAME = 'way_line'
public useHtmlLabel = false
pointMaterial: THREE.Material pointMaterial: THREE.Material
lineMaterial = new THREE.MeshBasicMaterial({ lineMaterial = new THREE.MeshBasicMaterial({
color: 0xa0cfff, color: 0xa0cfff,
@ -167,7 +165,7 @@ export default class WayRenderer extends BaseRenderer {
dispose() { dispose() {
super.dispose() super.dispose()
this.pointMaterial.dispose() this.pointMaterial?.dispose()
this.lineMaterial.dispose() this.lineMaterial?.dispose()
} }
} }

75
src/utils/webutils.ts

@ -7,6 +7,61 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import * as THREE from 'three' import * as THREE from 'three'
import Decimal from 'decimal.js' import Decimal from 'decimal.js'
/**
*
*/
export function getFreezeDeep<T>(obj: T, deep = 5): T {
// 辅助函数:深拷贝
function deepClone<T>(value: T, depth: number): T {
if (typeof value !== 'object' || value === null || depth < 0) {
return value
}
if (Array.isArray(value)) {
const arr: any[] = []
for (let i = 0; i < value.length; i++) {
arr[i] = deepClone(value[i], depth - 1)
}
return arr as any
}
const clone = {} as T
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
clone[key] = deepClone(value[key], depth - 1)
}
}
return clone
}
// 先深拷贝,再冻结
const cloned = deepClone(obj, deep)
function freezeDeep<T>(value: T, depth: number): T {
if (typeof value !== 'object' || value === null || depth < 0) {
return value
}
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
value[i] = freezeDeep(value[i], depth - 1)
}
} else {
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
value[key] = freezeDeep(value[key], depth - 1)
}
}
}
return Object.freeze(value)
}
return freezeDeep(cloned, deep)
}
export function getQueryParams() { export function getQueryParams() {
// const search = window.location.search || window.location.hash.split('?')[1] || '' // const search = window.location.search || window.location.hash.split('?')[1] || ''
// const params = new URLSearchParams(search) // const params = new URLSearchParams(search)
@ -68,31 +123,31 @@ export function createShortUUID() {
*/ */
export function compressUUID(uuid) { export function compressUUID(uuid) {
// 移除 UUID 中的连字符 // 移除 UUID 中的连字符
const hex = uuid.replace(/-/g, ''); const hex = uuid.replace(/-/g, '')
// 将 Hex 转换为十进制的大整数字符串 // 将 Hex 转换为十进制的大整数字符串
const decimalValue = new Decimal(`0x${hex}`); const decimalValue = new Decimal(`0x${hex}`)
// 定义 Base62 字符集 // 定义 Base62 字符集
const base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; const base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
let result = ''; let result = ''
let num = decimalValue; let num = decimalValue
// 使用 decimal.js 进行 Base62 转换 // 使用 decimal.js 进行 Base62 转换
while (num.greaterThanOrEqualTo(62)) { while (num.greaterThanOrEqualTo(62)) {
const remainder = num.mod(62); const remainder = num.mod(62)
result = base62Chars[remainder.toNumber()] + result; result = base62Chars[remainder.toNumber()] + result
num = num.dividedToIntegerBy(62); num = num.dividedToIntegerBy(62)
} }
if (num.toNumber() > 0) { if (num.toNumber() > 0) {
result = base62Chars[num.toNumber()] + result; result = base62Chars[num.toNumber()] + result
} }
// UUID 总共 16 字节,理论上最多是 128 bits,所以压缩后应该是 22 位 Base62 字符左右 // UUID 总共 16 字节,理论上最多是 128 bits,所以压缩后应该是 22 位 Base62 字符左右
// 补足前导 0 保证长度一致(可选) // 补足前导 0 保证长度一致(可选)
return result.padStart(22, '0'); return result.padStart(22, '0')
} }
/** /**

Loading…
Cancel
Save