Browse Source

添加选中实体 ID 以支持选中状态更新和实体删除处理

master
修宁 7 months ago
parent
commit
d8d0ed6880
  1. 86
      src/core/ModelUtils.ts
  2. 55
      src/core/base/BaseInteraction.ts
  3. 2
      src/core/controls/EsDragControls.ts
  4. 13
      src/core/controls/SelectInspect.ts
  5. 4
      src/core/manager/EntityManager.ts
  6. 16
      src/core/manager/InteractionManager.ts
  7. 21
      src/core/manager/StateManager.ts
  8. 6
      src/runtime/EventBus.ts

86
src/core/ModelUtils.ts

@ -153,21 +153,53 @@ export function escByKeyboard() {
return return
} }
viewport.state.cursorMode = 'normal' viewport.interactionManager.exitInteraction()
system.msg('操作已取消') system.msg('操作已取消')
} }
/**
* stateManager item
* , beginStateUpdate
* @param viewport
* @param point x,z
* @param distance
*/
export function findStateItemsByDistance(viewport: Viewport, point: Vector2, distance: number): ItemJson[] {
const result: ItemJson[] = []
for (const item of viewport.stateManager.vdata.items) {
// 安全校验 tf 结构
if (!item.tf || !Array.isArray(item.tf) || item.tf[0].length < 3) {
continue
}
const [x, , z] = item.tf[0]
const itemPoint = new Vector2(x, z)
const dist = itemPoint.distanceTo(point)
if (dist <= distance) {
result.push(item)
}
}
// 按距离升序排序(近距离优先)
return result.sort((a, b) => {
const aPos = new Vector2(a.tf[0][0], a.tf[0][2])
const bPos = new Vector2(b.tf[0][0], b.tf[0][2])
return aPos.distanceTo(point) - bPos.distanceTo(point)
})
}
export function quickCopyByMouse() { export function quickCopyByMouse() {
// 获取鼠标位置,查看鼠标是否在某个 viewport 的画布上,并取得该 viewport // 获取鼠标位置,查看鼠标是否在某个 viewport 的画布上,并取得该 viewport
const currentMouseInfo = window['CurrentMouseInfo'] if (!CurrentMouseInfo?.viewport || !CurrentMouseInfo.x || !CurrentMouseInfo.z) {
if (!currentMouseInfo?.viewport || !currentMouseInfo.x || !currentMouseInfo.z) {
system.msg('无法获取鼠标位置') system.msg('无法获取鼠标位置')
return return
} }
const x = currentMouseInfo.x const x = CurrentMouseInfo.x
const z = currentMouseInfo.z const z = CurrentMouseInfo.z
const viewport: Viewport = currentMouseInfo.viewport const viewport: Viewport = CurrentMouseInfo.viewport
// const point: THREE.Vector2 = currentMouseInfo.mouse // const point: THREE.Vector2 = currentMouseInfo.mouse
// //
// const ray = new THREE.Raycaster() // const ray = new THREE.Raycaster()
@ -181,26 +213,34 @@ export function quickCopyByMouse() {
// console.log('intersections:', intersections) // console.log('intersections:', intersections)
// 如果不在线上,查找0.2米内的有效点 Object3D, 如果有,则以这个点为起点, 延伸同类型的点,并让他们相连 // 如果不在线上,查找0.2米内的有效点 Object3D, 如果有,则以这个点为起点, 延伸同类型的点,并让他们相连
const r = findObject3DByCondition(viewport.scene, object => { const items = findStateItemsByDistance(viewport, new Vector2(x, z), 0.2)
// 判断 object 是否是有效的 Object3D, 并且是当前 viewport 的对象 if (items[0]) {
if (object instanceof THREE.Object3D && object.visible &&
object.userData.type && viewport.toolbox[object.userData.type]) {
const toolbox: Toolbox = viewport.toolbox[object.userData.type]
// 检查是否在 0.2 米内
const distance = object.position.distanceTo(new THREE.Vector3(x, 0, z))
if (distance < 0.2) {
// 找到一个有效点,执行复制操作 // 找到一个有效点,执行复制操作
viewport.toolStartObject = object viewport.interactionManager.startInteraction(items[0].t, { startPoint: items[0].id })
viewport.state.cursorMode = object.userData.type
// toolbox.start(object)
system.msg('连线成功') system.msg('连线成功')
return true return
}
} }
return false
}) // const r = findObject3DByCondition(viewport.scene, object => {
// // 判断 object 是否是有效的 Object3D, 并且是当前 viewport 的对象
// if (object instanceof THREE.Object3D && object.visible &&
// object.userData.type && viewport.toolbox[object.userData.type]) {
//
// const toolbox: Toolbox = viewport.toolbox[object.userData.type]
//
// // 检查是否在 0.2 米内
// const distance = object.position.distanceTo(new THREE.Vector3(x, 0, z))
// if (distance < 0.2) {
// // 找到一个有效点,执行复制操作
// viewport.toolStartObject = object
// viewport.state.cursorMode = object.userData.type
// // toolbox.start(object)
// system.msg('连线成功')
// return true
// }
// }
// return false
// })
if (!r || r.length === 0) { if (!r || r.length === 0) {
system.msg('鼠标所在位置,没有可复制的对象') system.msg('鼠标所在位置,没有可复制的对象')

55
src/core/base/BaseInteraction.ts

@ -120,10 +120,10 @@ export default abstract class BaseInteraction {
this.viewerDom = this.viewport.viewerDom this.viewerDom = this.viewport.viewerDom
this.canvas = this.viewport.renderer.domElement this.canvas = this.viewport.renderer.domElement
if (option.startPoint) { if (option.startPoint) {
this.linkStartPointId = option.startPoint.userData?.entityId this.linkStartPointId = option.startPoint
if (!this.linkStartPointId) {
this.linkStartPointObject = this.viewport.entityManager.findObjectsById(this.linkStartPointId)?.[0]
} }
if (this.linkStartPointId) {
this.linkStartPointObject = this.viewport.entityManager.findObjectsById(this.linkStartPointId)?.[0]
} }
pdFn = this.mousedown.bind(this) pdFn = this.mousedown.bind(this)
@ -264,13 +264,44 @@ export default abstract class BaseInteraction {
} }
this.lastClickTime = now this.lastClickTime = now
const from = this.viewport.stateManager.findItemById(this.linkStartPointId)
if (!from) {
system.showErrorDialog(`Cannot find state item: ${this.linkStartPointId}`)
return
}
// 如果正式的点命中到同类型的节点上,则不添加新的点,只牵线到该点
let catchPoint: ItemJson | null = this.viewport.stateManager.findItemByPosition(point, this.itemTypeName)
if (catchPoint) {
// 连线到目标点
if (this.linkStartPointId === catchPoint.id) {
// 自己连接自己,忽略
system.msg('Cannot link to itself.')
return
}
// 关联2个点
if (this.linkStartPointId && from) {
catchPoint.dt.center.push(this.linkStartPointId)
from.dt.center.push(catchPoint.id)
}
// 提交状态管理器
const stateManager = this.viewport.stateManager
stateManager.beginStateUpdate({ createFromInteraction: true })
catchPoint.dt.center.push(this.linkStartPointId)
from.dt.center.push(catchPoint.id)
stateManager.endStateUpdate()
} else {
const renderer = getRenderer(this.itemTypeName) const renderer = getRenderer(this.itemTypeName)
const defaultScale = renderer.getDefaultScale() const defaultScale = renderer.getDefaultScale()
const defaultRotation = renderer.getDefaultRotation() const defaultRotation = renderer.getDefaultRotation()
// 添加正式点 // 添加正式点
const itemJson = { catchPoint = {
id: system.createUUID(), id: system.createUUID(),
t: this.itemTypeName, t: this.itemTypeName,
v: true, v: true,
@ -286,25 +317,23 @@ export default abstract class BaseInteraction {
} }
} as ItemJson } as ItemJson
// 关联2个点
const from = this.viewport.entityManager.findItemById(this.linkStartPointId)
if (this.linkStartPointId && from) {
itemJson.dt.center.push(this.linkStartPointId)
from.dt.center.push(itemJson.id)
}
// 提交状态管理器 // 提交状态管理器
const stateManager = this.viewport.stateManager const stateManager = this.viewport.stateManager
stateManager.beginStateUpdate({ createFromInteraction: true }) stateManager.beginStateUpdate({ createFromInteraction: true })
stateManager.vdata.items.push(itemJson) // 关联2个点
stateManager.vdata.items.push(catchPoint)
catchPoint.dt.center.push(this.linkStartPointId)
from.dt.center.push(catchPoint.id)
stateManager.endStateUpdate() stateManager.endStateUpdate()
}
// 把点加入拖拽控制器 // 把点加入拖拽控制器
// this.viewport.dragControl.setDragObjects(marker, 'push') // this.viewport.dragControl.setDragObjects(marker, 'push')
// 更新起始点为新添加的点 // 更新起始点为新添加的点
this.linkStartPointId = itemJson.id this.linkStartPointId = catchPoint.id
this.linkStartPointObject = this.viewport.entityManager.findObjectsById(itemJson.id)?.[0] this.linkStartPointObject = this.viewport.entityManager.findObjectsById(catchPoint.id)?.[0]
// 删除临时点 // 删除临时点
this.tempPointMarker && this.viewport.scene.remove(this.tempPointMarker) this.tempPointMarker && this.viewport.scene.remove(this.tempPointMarker)

2
src/core/controls/EsDragControls.ts

@ -151,6 +151,8 @@ export default class EsDragControls {
const ret = this.currentInteraction?.dragPointComplete(this.viewport, e) const ret = this.currentInteraction?.dragPointComplete(this.viewport, e)
if (!ret) return if (!ret) return
EventBus.dispatch('selectedObjectPropertyChanged', {})
// 拖拽结束启用其他控制器 // 拖拽结束启用其他控制器
this.viewport.controls.enabled = true this.viewport.controls.enabled = true
this.isDragging = false this.isDragging = false

13
src/core/controls/SelectInspect.ts

@ -47,6 +47,11 @@ export default class SelectInspect implements IControls {
*/ */
recStartPos: THREE.Vector3 | null recStartPos: THREE.Vector3 | null
/**
* ID
*/
selectionId: string
constructor() { constructor() {
} }
@ -54,6 +59,7 @@ export default class SelectInspect implements IControls {
this.viewport = viewport this.viewport = viewport
this.canvas = this.viewport.renderer.domElement as HTMLCanvasElement this.canvas = this.viewport.renderer.domElement as HTMLCanvasElement
// 监听 shift 按住之后的矩形
pdFn = this.onMouseDown.bind(this) pdFn = this.onMouseDown.bind(this)
this.canvas.addEventListener('pointerdown', pdFn) this.canvas.addEventListener('pointerdown', pdFn)
pmFn = this.onMouseMove.bind(this) pmFn = this.onMouseMove.bind(this)
@ -65,11 +71,15 @@ export default class SelectInspect implements IControls {
this.updateSelectionBox(this.viewport.state.selectedObject) this.updateSelectionBox(this.viewport.state.selectedObject)
}) })
EventBus.on('selectedObjectPropertyChanged', (data) => {
this.updateSelectionBox(this.viewport.state.selectedObject)
})
EventBus.on('entityDeleted', (data) => { EventBus.on('entityDeleted', (data) => {
const id = data.deleteEntityId const id = data.deleteEntityId
// 如果删除的是当前选中对象,则清除选中状态 // 如果删除的是当前选中对象,则清除选中状态
if (this.viewport.state.selectedEntityId === id) { if (this.selectionId === id) {
this.updateSelectionBox(null) this.updateSelectionBox(null)
} }
}) })
@ -84,6 +94,7 @@ export default class SelectInspect implements IControls {
if (!selectedObject) { if (!selectedObject) {
return return
} }
this.selectionId = selectedObject.userData?.entityId
const expandAmount = 0.2 // 扩展包围盒的大小 const expandAmount = 0.2 // 扩展包围盒的大小
// 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb) // 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb)

4
src/core/manager/EntityManager.ts

@ -506,6 +506,10 @@ export default class EntityManager {
} }
return this.entities.get(linkStartPointId) return this.entities.get(linkStartPointId)
} }
getEntityMap() {
return this.entities
}
} }
interface LineDiffItem { interface LineDiffItem {

16
src/core/manager/InteractionManager.ts

@ -9,7 +9,7 @@ export interface InteractionOption {
/** /**
* *
*/ */
startPoint?: THREE.Object3D startPoint?: string
} }
/** /**
@ -24,7 +24,7 @@ export default class InteractionManager implements IControls {
currentTool: BaseInteraction | null = null currentTool: BaseInteraction | null = null
//搭配 state.cursorMode = xxx 之后, currentTool.start(第一个参数) 使用 //搭配 state.cursorMode = xxx 之后, currentTool.start(第一个参数) 使用
toolStartObject: THREE.Object3D | null = null option: InteractionOption | null = null
init(viewport: Viewport) { init(viewport: Viewport) {
this.viewport = viewport this.viewport = viewport
@ -40,7 +40,7 @@ export default class InteractionManager implements IControls {
* *
*/ */
startInteraction(itemType: string, option: InteractionOption = {}): void { startInteraction(itemType: string, option: InteractionOption = {}): void {
this._setActiveInteraction(itemType, option.startPoint) this._setActiveInteraction(itemType, option)
} }
/** /**
@ -59,7 +59,7 @@ export default class InteractionManager implements IControls {
system.msg('退出新建模式') system.msg('退出新建模式')
} }
private _setActiveInteraction(mode: string, startPoint?: THREE.Object3D): void { private _setActiveInteraction(mode: string, option?: InteractionOption): void {
const state = this.viewport.state const state = this.viewport.state
if (!state.isReady) return if (!state.isReady) return
@ -70,7 +70,7 @@ export default class InteractionManager implements IControls {
} }
// 如果已有相同类型的交互,并且起点一致,无需重复初始化 // 如果已有相同类型的交互,并且起点一致,无需重复初始化
if (this.currentTool?.itemTypeName === mode && this.toolStartObject === startPoint) { if (this.currentTool?.itemTypeName === mode) {
return return
} }
@ -85,15 +85,15 @@ export default class InteractionManager implements IControls {
} }
// 保存参数 // 保存参数
if (startPoint) { if (option) {
this.toolStartObject = startPoint this.option = option
} }
// 初始化交互 // 初始化交互
this.currentTool = interaction this.currentTool = interaction
this.viewport.dragControl.dragControls.enabled = false this.viewport.dragControl.dragControls.enabled = false
this.currentTool.start(this.viewport, { startPoint: this.toolStartObject }) this.currentTool.start(this.viewport, this.option)
// 更新 UI 状态 // 更新 UI 状态
this.viewport.viewerDom.style.cursor = 'crosshair' this.viewport.viewerDom.style.cursor = 'crosshair'

21
src/core/manager/StateManager.ts

@ -1,4 +1,5 @@
import _ from 'lodash' import _ from 'lodash'
import * as THREE from 'three'
import localforage from 'localforage' import localforage from 'localforage'
import type EntityManager from './EntityManager' import type EntityManager from './EntityManager'
import { markRaw, reactive, ref } from 'vue' import { markRaw, reactive, ref } from 'vue'
@ -545,6 +546,26 @@ export default class StateManager {
success: false success: false
} }
} }
/**
* ID
*/
findItemById(linkStartPointId: string): ItemJson | undefined {
return _.find(this.vdata.items, item => item.id === linkStartPointId)
}
/**
* ,
*/
findItemByPosition(point: THREE.Vector3, itemTypeName?: string): ItemJson | undefined {
for (const item of this.vdata.items) {
if (item.tf?.[0]?.[0] === point.x && item.tf?.[0]?.[2] === point.z) {
if (!itemTypeName || item.t === itemTypeName) {
return item
}
}
}
}
} }
export interface StateUpdateOption { export interface StateUpdateOption {

6
src/runtime/EventBus.ts

@ -2,7 +2,11 @@ import mitt from 'mitt'
const instance = mitt() const instance = mitt()
export type DispatchNames = 'selectedObjectChanged' | 'catalogChanged' | 'dataLoadComplete' | 'entityDeleted' export type DispatchNames = 'selectedObjectChanged' |
'catalogChanged' |
'dataLoadComplete' |
'entityDeleted' |
'selectedObjectPropertyChanged'
export default { export default {
dispatch(name: DispatchNames, data?: any) { dispatch(name: DispatchNames, data?: any) {

Loading…
Cancel
Save