Browse Source

mousemove

master
修宁 6 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. 77
      src/utils/webutils.ts

5
src/core/base/BaseRenderer.ts

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

4
src/core/controls/SelectInspect.ts

@ -312,11 +312,11 @@ export default class SelectInspect implements IControls {
if (Date.now() - clickTime < 200) {
// 如果是点击事件,触发选中逻辑
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)
const object = objects[0]
const entityId = object.userData.entityId
const item = this.viewport.entityManager.findItemById(entityId)
let item = this.viewport.entityManager.findItemById(entityId)
const itemTypeName = object.userData.t
if (item.dt.protected !== true) {
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 { getClosestObject, getLineId, parseLineId } from '@/core/ModelUtils'
import { Vector2 } from 'three'
import { getFreezeDeep } from '@/utils/webutils.ts'
/**
*
@ -17,7 +18,7 @@ export default class EntityManager {
viewport: Viewport
// 所有数据点的实体
private readonly entities = new Map<string, ItemJson>()
private readonly ___entityMap = new Map<string, ItemJson>()
// 关系索引
private readonly relationIndex = new Map<string, Relation>()
@ -26,7 +27,9 @@ export default class EntityManager {
private readonly objects = new Map<string, 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 数组
private readonly lines = new Map<string, THREE.Object3D[]>()
@ -51,7 +54,7 @@ export default class EntityManager {
this.diffRenderer.clear()
// 清理所有实体和关系索引
this.entities.clear()
this.___entityMap.clear()
this.relationIndex.clear()
this.objects.clear()
this.lines.clear()
@ -62,14 +65,14 @@ export default class EntityManager {
*
*/
cloneWriteBackEntities(): Map<string, ItemJson> {
const entities = new Map<string, ItemJson>()
const result = new Map<string, ItemJson>()
for (const id of this.writeBackEntities) {
const entity = this.entities.get(id)
const entity = this.___entityMap.get(id)
if (!entity) continue
entities.set(id, _.cloneDeep(entity))
result.set(id, _.cloneDeep(entity))
}
console.log('需要回写', entities.size, '行数据')
return entities
console.log('需要回写', result.size, '行数据')
return result
}
init(viewport: Viewport) {
@ -97,14 +100,14 @@ export default class EntityManager {
throw new Error('Entity must have an id')
}
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 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)
@ -148,7 +151,7 @@ export default class EntityManager {
* , 线, , center[] / in[] / out[]
*/
deleteEntity(id: string, option: EntityCudOption = {}): void {
const entity = this.entities.get(id)
const entity = this.___entityMap.get(id)
if (!entity) return
option.originEntity = _.cloneDeep(entity)
@ -157,7 +160,7 @@ export default class EntityManager {
// 先生成线差量,再清理关系
this.generateLineDiffsForDelete(id)
this.removeRelations(id) // 清理关系
this.entities.delete(id) // 删除实体
this.___entityMap.delete(id) // 删除实体
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 [lineId, lineDiffItem] of this.lineDiffs.create.entries()) {
const start = this.entities.get(lineDiffItem.startId)
const end = this.entities.get(lineDiffItem.endId)
const start = this.___entityMap.get(lineDiffItem.startId)
const end = this.___entityMap.get(lineDiffItem.endId)
// 添加存在性检查
if (!start || !end) {
@ -207,8 +210,8 @@ export default class EntityManager {
// "线"更新
for (const [lineId, lineDiffItem] of this.lineDiffs.update.entries()) {
const start = this.entities.get(lineDiffItem.startId)
const end = this.entities.get(lineDiffItem.endId)
const start = this.___entityMap.get(lineDiffItem.startId)
const end = this.___entityMap.get(lineDiffItem.endId)
// 添加存在性检查
if (!start || !end) {
@ -226,8 +229,8 @@ export default class EntityManager {
// "线"删除
for (const [lineId, lineDiffItem] of this.lineDiffs.delete.entries()) {
const start = this.entities.get(lineDiffItem.startId)
const end = this.entities.get(lineDiffItem.endId)
const start = this.___entityMap.get(lineDiffItem.startId)
const end = this.___entityMap.get(lineDiffItem.endId)
if (!start || !end) {
// 即使实体不存在也要处理删除
@ -265,7 +268,7 @@ export default class EntityManager {
needUpdateIds.add(lineDiffItem.endId)
}
for (const id of needUpdateIds) {
const entity = this.entities.get(id)
const entity = this.___entityMap.get(id)
if (entity) {
entity.dt.center = Array.from(this.relationIndex.get(id)?.center || [])
entity.dt.in = Array.from(this.relationIndex.get(id)?.input || [])
@ -407,115 +410,45 @@ export default class EntityManager {
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) {
return this.entities.delete(id)
return this.___entityMap.delete(id)
}
findObjectsById(id: string) {
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[]) {
this.objects.set(id, points)
// 如果是可选中对象,添加到 _selectableObjects 中
if (points.some(obj => obj.userData.selectable !== false)) {
this._selectableObjects.push(...points)
for (const point of points) {
if (point.userData.selectable !== false) {
this._selectableObjects.push(point)
}
if (point.userData.draggable) {
this._draggableObjects.push(point)
}
}
}
appendLineObject(id: string, lines: THREE.Object3D[]) {
this.lines.set(id, lines)
// 如果是可选中对象,添加到 _selectableObjects 中
if (lines.some(obj => obj.userData.selectable !== false)) {
this._selectableObjects.push(...lines)
}
}
findLineObjectsById(lineId: string): THREE.Object3D[] {
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) {
// 删除线对象时,也需要从 _selectableObjects 中移除
const rel = this.lines.get(id)
if (rel) {
_.remove(this._selectableObjects, obj => !rel.includes(obj))
}
_.remove(this._selectableObjects, obj => obj.userData?.entityId === id)
_.remove(this._draggableObjects, obj => obj.userData?.entityId === id)
return this.lines.delete(id)
}
@ -526,7 +459,7 @@ export default class EntityManager {
if (!linkStartPointId) {
return
}
return this.entities.get(linkStartPointId)
return getFreezeDeep(this.___entityMap.get(linkStartPointId))
}
getObjectByCanvasMouse(event: MouseEvent): THREE.Object3D[] {

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

@ -1,7 +1,8 @@
<template>
<div class="title">
<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">
<template #prefix>
<component :is="renderIcon('element Search')"></component>
@ -85,7 +86,7 @@ export default {
},
computed: {
t() {
return this.selectedItem ? ('(' + this.selectedItem.t + ')') : ''
return this.selectedItem ? this.selectedItem.t : ''
},
selectedItem() {
return this.state?.selectedItem

2
src/modules/gstore/GstoreRenderer.ts

@ -101,6 +101,6 @@ export default class GstoreRenderer extends BaseRenderer {
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.pointMaterial.dispose()
this.lineMaterial.dispose()
this.pointMaterial?.dispose()
this.lineMaterial?.dispose()
}
}

2
src/modules/rack/RackRenderer.ts

@ -137,7 +137,7 @@ export default class RackRenderer extends BaseRenderer {
dispose() {
super.dispose()
this.pointMaterial.dispose()
this.pointMaterial?.dispose()
}
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 LINE_NAME = 'way_line'
public useHtmlLabel = false
pointMaterial: THREE.Material
lineMaterial = new THREE.MeshBasicMaterial({
color: 0xa0cfff,
@ -167,7 +165,7 @@ export default class WayRenderer extends BaseRenderer {
dispose() {
super.dispose()
this.pointMaterial.dispose()
this.lineMaterial.dispose()
this.pointMaterial?.dispose()
this.lineMaterial?.dispose()
}
}

77
src/utils/webutils.ts

@ -7,6 +7,61 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import * as THREE from 'three'
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() {
// const search = window.location.search || window.location.hash.split('?')[1] || ''
// const params = new URLSearchParams(search)
@ -68,31 +123,31 @@ export function createShortUUID() {
*/
export function compressUUID(uuid) {
// 移除 UUID 中的连字符
const hex = uuid.replace(/-/g, '');
const hex = uuid.replace(/-/g, '')
// 将 Hex 转换为十进制的大整数字符串
const decimalValue = new Decimal(`0x${hex}`);
const decimalValue = new Decimal(`0x${hex}`)
// 定义 Base62 字符集
const base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
let result = '';
let num = decimalValue;
let result = ''
let num = decimalValue
// 使用 decimal.js 进行 Base62 转换
while (num.greaterThanOrEqualTo(62)) {
const remainder = num.mod(62);
result = base62Chars[remainder.toNumber()] + result;
num = num.dividedToIntegerBy(62);
const remainder = num.mod(62)
result = base62Chars[remainder.toNumber()] + result
num = num.dividedToIntegerBy(62)
}
if (num.toNumber() > 0) {
result = base62Chars[num.toNumber()] + result;
result = base62Chars[num.toNumber()] + result
}
// UUID 总共 16 字节,理论上最多是 128 bits,所以压缩后应该是 22 位 Base62 字符左右
// 补足前导 0 保证长度一致(可选)
return result.padStart(22, '0');
return result.padStart(22, '0')
}
/**
@ -178,4 +233,4 @@ export function renderIcon(icon: string, props = {}): any {
return undefined
}
return () => h(ElIcon, props, { default: () => h(component) })
}
}

Loading…
Cancel
Save