Browse Source

EsDragControl 延迟抓取

master
修宁 6 months ago
parent
commit
829dbd6fc1
  1. 2
      README.md
  2. 277
      src/core/controls/DragControl.ts
  3. 255
      src/core/controls/DragControls.js
  4. 163
      src/core/controls/EsDragControls.ts
  5. 25
      src/core/controls/SelectInspect.ts
  6. 8
      src/core/engine/Viewport.ts
  7. 29
      src/modules/gstore/GstoreRenderer.ts

2
README.md

@ -128,7 +128,7 @@ src/
│ │ └── ... │ │ └── ...
│ ├── controls/ # 各种实体基础类 │ ├── controls/ # 各种实体基础类
│ │ ├── SelectionControls.ts │ │ ├── SelectionControls.ts
│ │ ├── EsDragControls.ts │ │ ├── DragControl.ts
│ │ └── MouseMoveControls.ts │ │ └── MouseMoveControls.ts
│ ├── Model3DViewer.vue │ ├── Model3DViewer.vue
│ ├── Model2DEditor.vue │ ├── Model2DEditor.vue

277
src/core/controls/EsDragControl2.ts → src/core/controls/DragControl.ts

@ -1,29 +1,29 @@
import * as THREE from 'three' import * as THREE from 'three'
import type Viewport from '@/core/engine/Viewport.ts' import type Viewport from '@/core/engine/Viewport.ts'
import type IControls from '@/core/controls/IControls.ts' import type IControls from '@/core/controls/IControls.ts'
import { Curve } from 'three'
import { getClosestObject } from '@/core/ModelUtils.ts' import { getClosestObject } from '@/core/ModelUtils.ts'
import EventBus from '@/runtime/EventBus.ts' import EventBus from '@/runtime/EventBus.ts'
/** /**
* DragControl2 - ThreeJS X/Z * ThreeJS X/Z
*/ */
export default class DragControl2 implements IControls { export default class DragControl implements IControls {
private viewport: Viewport private viewport: Viewport
private _is_enabled: boolean = true private _is_enabled: boolean = true
private domElement: HTMLElement private domElement: HTMLElement
public isDragging: boolean = false public isDragging: boolean = false
private isPointerDown: boolean = false private isPointerDown: boolean = false
private dragStartMouse: THREE.Vector2 = new THREE.Vector2() private dragStartMouse: THREE.Vector2 = new THREE.Vector2()
private checkStateInterval: number | null = null
private dragShadows: THREE.Group | null = null private dragShadowsGroup: THREE.Group | null = null
private dragDelayTimeout: number | null = null
init(viewport: Viewport): void { init(viewport: Viewport): void {
this.viewport = viewport this.viewport = viewport
const domElement = this.viewport.renderer.domElement const domElement = this.viewport.renderer.domElement
domElement.addEventListener('pointermove', this.onPointerMove)
domElement.addEventListener('pointerdown', this.onPointerDown) domElement.addEventListener('pointerdown', this.onPointerDown)
domElement.addEventListener('pointermove', this.onPointerMove)
domElement.addEventListener('pointerup', this.onPointerUp) domElement.addEventListener('pointerup', this.onPointerUp)
domElement.addEventListener('pointerleave', this.onPointerLeave) domElement.addEventListener('pointerleave', this.onPointerLeave)
@ -33,20 +33,6 @@ export default class DragControl2 implements IControls {
/** /**
* /
*/
public set enabled(value: boolean) {
this._is_enabled = value
if (!value) {
this.cleanupDrag()
}
}
public get enabled(): boolean {
return this._is_enabled
}
/**
* *
*/ */
dispose(): void { dispose(): void {
@ -60,6 +46,89 @@ export default class DragControl2 implements IControls {
this.domElement.style.cursor = 'auto' this.domElement.style.cursor = 'auto'
this.viewport = null this.viewport = null
} }
}
/**
* pointerdown
*/
private onPointerDown = (event: PointerEvent): void => {
if (!this.enabled) return
const mouse = this.getMousePosition(event.clientX, event.clientY)
const intersected = this.getIntersectedDraggableObject(mouse)
if (!intersected) return
this.viewport.controls.enabled = false
// 设置定时器:0.1秒后才抓取拖拽
this.dragDelayTimeout = window.setTimeout(() => {
const mouse = this.getMousePosition(event.clientX, event.clientY)
const intersected = this.getIntersectedDraggableObject(mouse)
if (!intersected) return
this.isPointerDown = true
this.dragStartMouse.set(intersected.position.x, intersected.position.z)
let selectedObjects = [intersected]
if (this.viewport.state.multiSelectedObjects.length > 0) {
if (this.viewport.state.multiSelectedObjects.includes(intersected)) {
// drag multi-selected objects
selectedObjects = this.viewport.state.multiSelectedObjects
}
}
this.checkStateInterval = setInterval(() => {
if (isNaN(CurrentMouseInfo.x) || isNaN(CurrentMouseInfo.z) || !this.isPointerDown) {
this.cancelDrag()
}
}, 100)
this.createShadows(selectedObjects)
this.domElement.style.cursor = 'grabbing'
}, 100) // 0.1秒延迟抓取
}
/**
* pointermove
*/
private onPointerMove = (event: PointerEvent): void => {
if (!this.enabled || !this.domElement) return
if (!isNaN(this.dragStartMouse.x) && !isNaN(this.dragStartMouse.y) && this.dragShadowsGroup) {
this.isDragging = true
this.domElement.style.cursor = 'grabbing'
// 更新阴影位置(锁定 Y 轴)
this.updateShadows(new THREE.Vector2(CurrentMouseInfo.x, CurrentMouseInfo.z))
} else {
const mouse = this.getMousePosition(event.clientX, event.clientY)
const intersected = this.getIntersectedDraggableObject(mouse)
if (intersected) {
this.domElement.style.cursor = 'grab'
} else {
this.domElement.style.cursor = 'auto'
}
}
}
/**
* pointerup
*/
private onPointerUp = (event: PointerEvent): void => {
if (this.isDragging) {
const startPos = this.dragStartMouse
const targetPos = new THREE.Vector2(CurrentMouseInfo.x, CurrentMouseInfo.z)
if (startPos && targetPos) {
this.dragComplete(startPos, targetPos)
}
}
this.cleanupDrag()
} }
/** /**
@ -68,10 +137,26 @@ export default class DragControl2 implements IControls {
private cleanupDrag(): void { private cleanupDrag(): void {
if (this.domElement) { if (this.domElement) {
this.domElement.style.cursor = 'auto' this.domElement.style.cursor = 'auto'
this.isDragging = false }
this.isPointerDown = false
this.dragStartMouse.set(NaN, NaN) if (this.viewport) {
this.removeShadows() this.viewport.controls.enabled = true
}
this.isDragging = false
this.isPointerDown = false
this.dragStartMouse.set(NaN, NaN)
this.removeShadows()
if (this.dragDelayTimeout !== null) {
clearTimeout(this.dragDelayTimeout)
this.dragDelayTimeout = null
}
if (this.checkStateInterval) {
clearInterval(this.checkStateInterval)
this.checkStateInterval = null
} }
} }
@ -101,6 +186,14 @@ export default class DragControl2 implements IControls {
} }
} }
private static readonly SHADOW_MATERIAL = new THREE.MeshBasicMaterial({
color: 0x222222,
transparent: true,
opacity: 0.5,
depthWrite: false,
side: THREE.DoubleSide
})
/** /**
* *
*/ */
@ -109,32 +202,44 @@ export default class DragControl2 implements IControls {
console.log('createShadows', objects.map(obj => obj.name)) console.log('createShadows', objects.map(obj => obj.name))
this.dragShadows = new THREE.Group() this.dragShadowsGroup = new THREE.Group()
// 初始位置为 (0, 0, 0)
this.dragShadowsGroup.position.set(0, 0, 0)
for (const obj of objects) { for (const obj of objects) {
if (!obj.userData.entityId) { if (!obj.userData.entityId) {
console.error('Object does not have entityId:', obj.name) console.error('Object does not have entityId:', obj.name)
continue // 跳过没有 entityId 的对象 continue // 跳过没有 entityId 的对象
} }
const shadow = obj.clone() // 克隆对象的几何体和材质
// const shadowBox = obj.clone()
const box = new THREE.Box3().setFromObject(obj)
const size = new THREE.Vector3()
box.getSize(size)
const geometry = new THREE.PlaneGeometry(size.x, size.z)
const shadowBox = new THREE.Mesh(geometry, DragControl.SHADOW_MATERIAL)
shadowBox.position.copy(obj.position)
shadowBox.rotation.x = -Math.PI / 2
shadow.userData = { shadowBox.userData = {
isShadow: true, isShadow: true,
entityId: obj.userData.entityId, entityId: obj.userData.entityId,
originPosition: obj.position.clone() // 保存原始位置 originPosition: obj.position.clone() // 保存原始位置
} }
this.dragShadows.add(shadow) this.dragShadowsGroup.add(shadowBox)
} }
this.viewport.scene.add(this.dragShadows) this.viewport.scene.add(this.dragShadowsGroup)
} }
/** /**
* *
*/ */
private removeShadows(): void { private removeShadows(): void {
if (this.dragShadows) { if (this.dragShadowsGroup) {
this.viewport.scene.remove(this.dragShadows) console.log('removeShadows')
this.dragShadows = null this.viewport.scene.remove(this.dragShadowsGroup)
this.dragShadowsGroup = null
} }
} }
@ -142,25 +247,30 @@ export default class DragControl2 implements IControls {
* X/Z * X/Z
*/ */
private updateShadows(newPosition: THREE.Vector2): void { private updateShadows(newPosition: THREE.Vector2): void {
if (!this.dragShadows) return if (!this.dragShadowsGroup) return
// 计算新位置与拖拽开始位置的偏移量 // 计算新位置与拖拽开始位置的偏移量
const offsetX = newPosition.x - this.dragStartMouse.x const offsetX = newPosition.x - this.dragStartMouse.x
const offsetZ = newPosition.y - this.dragStartMouse.y const offsetZ = newPosition.y - this.dragStartMouse.y
this.dragShadows.children.forEach((shadow, index) => { for (let i = 0; i < this.dragShadowsGroup.children.length; i++) {
const shadow = this.dragShadowsGroup.children[i] as THREE.Mesh
if (!shadow.userData.originPosition) {
console.error(`Shadow ${shadow.name} does not have originPosition`)
continue
}
const newPosX = shadow.userData.originPosition.x + offsetX const newPosX = shadow.userData.originPosition.x + offsetX
const newPosZ = shadow.userData.originPosition.z + offsetZ const newPosZ = shadow.userData.originPosition.z + offsetZ
shadow.position.set(newPosX, shadow.userData.originPosition.y, newPosZ) // 锁定 Y 轴高度 shadow.position.set(newPosX, shadow.userData.originPosition.y, newPosZ) // 锁定 Y 轴高度
console.log(`Updated shadow position for ${shadow.name}:`, shadow.position) // console.log(`Updating shadow ${shadow.name} position with offset:`, offsetX, offsetZ)
}) }
} }
private dragComplete = (startPos: THREE.Vector2, targetPos: THREE.Vector2): void => { private dragComplete = (startPos: THREE.Vector2, targetPos: THREE.Vector2): void => {
// console.log(`Drag completed from ${startPos.toArray()} to ${targetPos.toArray()}`) // console.log(`Drag completed from ${startPos.toArray()} to ${targetPos.toArray()}`)
this.viewport.stateManager.beginStateUpdate() this.viewport.stateManager.beginStateUpdate()
for (const object of this.dragShadows.children) { for (const object of this.dragShadowsGroup.children) {
const entityId = object.userData.entityId const entityId = object.userData.entityId
if (entityId) { if (entityId) {
const entity = this.viewport.stateManager.findItemById(entityId) const entity = this.viewport.stateManager.findItemById(entityId)
@ -186,96 +296,31 @@ export default class DragControl2 implements IControls {
} }
/** /**
* pointermove * pointerleave
*/ */
private onPointerMove = (event: PointerEvent): void => { private onPointerLeave = (_event: PointerEvent): void => {
if (!this.enabled || !this.domElement) return this.cancelDrag()
if (!isNaN(this.dragStartMouse.x) && !isNaN(this.dragStartMouse.y) && this.dragShadows) {
this.isDragging = true
// 更新鼠标样式
this.domElement.style.cursor = 'grabbing'
// 更新阴影位置(锁定 Y 轴)
this.updateShadows(new THREE.Vector2(CurrentMouseInfo.x, CurrentMouseInfo.z))
} else {
const mouse = this.getMousePosition(event.clientX, event.clientY)
const intersected = this.getIntersectedDraggableObject(mouse)
if (intersected) {
this.domElement.style.cursor = 'grab'
} else {
this.domElement.style.cursor = 'auto'
}
}
} }
/** /**
* pointerdown *
*/ */
private onPointerDown = (event: PointerEvent): void => { cancelDrag(): void {
if (!this.enabled) return this.cleanupDrag()
const mouse = this.getMousePosition(event.clientX, event.clientY)
const intersected = this.getIntersectedDraggableObject(mouse)
if (!intersected) return
this.isPointerDown = true
this.dragStartMouse.set(intersected.position.x, intersected.position.z)
this.viewport.controls.enabled = false
let selectedObjects = [intersected]
if (this.viewport.state.multiSelectedObjects.length > 0) {
if (this.viewport.state.multiSelectedObjects.includes(intersected)) {
// drag multi-selected objects
selectedObjects = this.viewport.state.multiSelectedObjects
}
}
this.createShadows(selectedObjects)
} }
/** /**
* pointerup * /
*/ */
private onPointerUp = (event: PointerEvent): void => { public set enabled(value: boolean) {
if (!this.enabled) return this._is_enabled = value
if (!value) {
this.dragStartMouse.set(NaN, NaN) this.cleanupDrag()
this.isPointerDown = false
// console.log('enable controls')
this.viewport.controls.enabled = true
if (this.isDragging) {
const startPos = this.dragStartMouse
const targetPos = new THREE.Vector2(CurrentMouseInfo.x, CurrentMouseInfo.z)
if (startPos && targetPos) {
this.dragComplete(startPos, targetPos)
}
} }
} }
/** public get enabled(): boolean {
* pointerleave return this._is_enabled
*/
private onPointerLeave = (_event: PointerEvent): void => {
if (!this.enabled) return
this.cleanupDrag()
}
/**
*
*/
cancelDrag(): void {
if (!this.domElement) return
if (this.isDragging || this.isPointerDown) {
this.cleanupDrag()
this.domElement.style.cursor = 'auto'
this.viewport.controls.enabled = true
}
} }
} }

255
src/core/controls/DragControls.js

@ -1,255 +0,0 @@
import { EventDispatcher, Matrix4, Plane, Raycaster, Vector2, Vector3 } from 'three'
import { calcPositionUseSnap, getClosestObject } from '@/core/ModelUtils.js'
const _plane = new Plane() // 用于拖拽操作的平面
const _raycaster = new Raycaster() // 射线检测器,用于拾取物体
const _pointer = new Vector2() // 屏幕坐标指针位置 (归一化设备坐标)
const _offset = new Vector3() // 拖动时相对于点击点的偏移量
const _intersection = new Vector3() // 与平面相交的点
const _worldPosition = new Vector3() // 世界坐标位置
const _inverseMatrix = new Matrix4() // 用于将位置转换到局部空间
/**
* DragControls 控制器类
* 提供基于鼠标或触摸的拖拽交互功能并支持 hover clickblank 等事件
*/
class DragControls extends EventDispatcher {
/**
* 构造函数
* @param _objects 可拖拽的对象数组
* @param _camera 当前使用的相机
* @param _domElement 绑定事件的目标 DOM 元素通常是 canvas
*/
constructor(_objects, _camera, _domElement) {
super()
// 禁止触摸滚动行为
_domElement.style.touchAction = 'none'
let _selected = null // 当前选中(正在拖动)的对象
let _hovered = null // 当前悬停的对象
const _intersections = [] // 存储射线检测结果的数组
let isMove = false // 标记是否发生了移动
let isMouseDownClicked = false // 标记是否按下了鼠标
const scope = this // 保存当前上下文
/**
* 激活事件监听器
*/
function activate() {
_domElement.addEventListener('pointermove', onPointerMove)
_domElement.addEventListener('pointerdown', onPointerDown)
_domElement.addEventListener('pointerup', onPointerCancel)
_domElement.addEventListener('pointerleave', onPointerCancel)
}
/**
* 去激活事件监听器
*/
function deactivate() {
_domElement.removeEventListener('pointermove', onPointerMove)
_domElement.removeEventListener('pointerdown', onPointerDown)
_domElement.removeEventListener('pointerup', onPointerCancel)
_domElement.removeEventListener('pointerleave', onPointerCancel)
_domElement.style.cursor = ''
}
/**
* 销毁控制器并释放资源
*/
function dispose() {
deactivate()
}
/**
* 设置可拖拽的对象列表
* @param {Array} objects - 新的对象数组
*/
function setObjects(objects) {
_objects = objects
}
/**
* 获取当前可拖拽的对象列表
* @returns {Array}
*/
function getObjects() {
return _objects
}
/**
* 获取内部的 Raycaster 实例
* @returns {Raycaster}
*/
function getRaycaster() {
return _raycaster
}
/**
* 鼠标/指针移动事件处理函数
* 处理悬停拖拽等交互逻辑
*/
function onPointerMove(event) {
if (!scope.enabled || !scope.enabledMove) return
if (isMouseDownClicked) {
_domElement.style.cursor = 'move'
}
isMove = true
updatePointer(event)
_raycaster.setFromCamera(_pointer, _camera)
// 如果有选中的对象,则更新其位置
if (_selected) {
if (_raycaster.ray.intersectPlane(_plane, _intersection)) {
const pos = _intersection.sub(_offset).applyMatrix4(_inverseMatrix)
const newIntersection = calcPositionUseSnap(event, pos)
_selected.position.copy(newIntersection)
}
scope.dispatchEvent({ type: 'drag', object: _selected })
return
}
// 鼠标/笔悬停检测
if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
_intersections.length = 0
_raycaster.setFromCamera(_pointer, _camera)
_raycaster.intersectObjects(_objects, true, _intersections)
if (_intersections.length > 0) {
const object = _intersections[0].object
_plane.setFromNormalAndCoplanarPoint(
_camera.getWorldDirection(_plane.normal),
_worldPosition.setFromMatrixPosition(object.matrixWorld)
)
if (_hovered !== object && _hovered !== null) {
scope.dispatchEvent({ type: 'hoveroff', object: _hovered })
_domElement.style.cursor = 'auto'
_hovered = null
}
if (_hovered !== object) {
scope.dispatchEvent({ type: 'hoveron', object: object })
_domElement.style.cursor = 'pointer'
_hovered = object
}
} else {
if (_hovered !== null) {
scope.dispatchEvent({ type: 'hoveroff', object: _hovered })
_domElement.style.cursor = 'auto'
_hovered = null
}
}
}
}
/**
* 鼠标按下事件处理函数
* 检测是否点击了可拖拽对象并准备开始拖拽
*/
function onPointerDown(event) {
if (scope.enabled === false) return
updatePointer(event)
_intersections.length = 0
_raycaster.setFromCamera(_pointer, _camera)
let objects = _objects
_raycaster.intersectObjects(objects, true, _intersections)
if (_intersections.length > 0) {
// 判断是否启用组拖动模式
_selected = (scope.transformGroup === true) ? _objects[0] : _intersections[0].object
if (scope.enabledMove) {
// 设置拖拽平面
_plane.setFromNormalAndCoplanarPoint(
_camera.getWorldDirection(_plane.normal),
_worldPosition.setFromMatrixPosition(_selected.matrixWorld)
)
if (_raycaster.ray.intersectPlane(_plane, _intersection)) {
// 计算偏移量
_inverseMatrix.copy(_selected.parent.matrixWorld).invert()
_offset.copy(_intersection).sub(
_worldPosition.setFromMatrixPosition(_selected.matrixWorld)
)
}
isMouseDownClicked = true
}
// 触发 dragstart 事件
scope.dispatchEvent({ type: 'dragstart', object: _selected, e: event })
}
isMove = false
}
/**
* 鼠标释放或离开事件处理函数
* 结束拖拽操作或触发点击空白区域事件
*/
function onPointerCancel(event) {
if (scope.enabled === false) return
if (_selected) {
// 结束拖拽
scope.dispatchEvent({ type: 'dragend', object: _selected, e: event })
_selected = null
} else if (!isMove) {
// 如果没有发生移动,则认为是点击空白处
scope.dispatchEvent({ type: 'clickblank', e: event })
}
// 恢复光标状态
_domElement.style.cursor = _hovered ? 'pointer' : 'auto'
isMouseDownClicked = false
}
/**
* 更新指针位置
* 将屏幕坐标转换为 NDC 设备坐标 (-1 ~ 1)
*/
function updatePointer(event) {
const rect = _domElement.getBoundingClientRect()
_pointer.x = (event.clientX - rect.left) / rect.width * 2 - 1
_pointer.y = -(event.clientY - rect.top) / rect.height * 2 + 1
}
// 初始化:激活事件监听器
activate()
// 暴露 API 方法和属性
this.enabled = true // 是否启用控制器
this.enabledMove = true // 是否允许移动操作
this.transformGroup = false // 是否以组形式变换多个对象
this.activate = activate
this.deactivate = deactivate
this.dispose = dispose
this.setObjects = setObjects
this.getObjects = getObjects
this.getRaycaster = getRaycaster
}
}
export { DragControls }

163
src/core/controls/EsDragControls.ts

@ -1,163 +0,0 @@
import * as THREE from 'three'
import { DragControls } from './DragControls.js'
import type Viewport from '@/core/engine/Viewport.ts'
import EventBus from '@/runtime/EventBus'
import { getInteraction } from '@/core/manager/ModuleManager.ts'
import type BaseInteraction from '@/core/base/BaseInteraction.ts'
// dragControls 绑定函数
let dragStartFn, dragFn, dragEndFn, clickblankFn
export default class EsDragControls {
_dragObjects: THREE.Object3D[] = [] // 拖拽对象
dragControls: any
private onDownPosition: { x: number; y: number } = { x: -1, y: -1 }
viewport: Viewport
currentInteraction: BaseInteraction
isDragging = false
constructor(viewport) {
this.viewport = viewport
// 物体拖拽控制器
this.dragControls = new DragControls(this._dragObjects, viewport.camera, viewport.renderer.domElement)
this.dragControls.deactivate() // 默认禁用
dragStartFn = this.dragControlsStart.bind(this)
this.dragControls.addEventListener('dragstart', dragStartFn)
dragFn = this.drag.bind(this)
this.dragControls.addEventListener('drag', dragFn)
dragEndFn = this.dragControlsEnd.bind(this)
this.dragControls.addEventListener('dragend', dragEndFn)
// 点击可拖拽物体之外
clickblankFn = this.clickblank.bind(this)
this.dragControls.addEventListener('clickblank', clickblankFn)
}
set domElement(element: HTMLElement) {
this.dragControls.setDomElement(element)
}
setDragObjects(objects: THREE.Object3D[], type: 'eq' | 'push' | 'remove' = 'eq') {
// 当前拖拽对象为空时加入对象需激活控制器
if (this._dragObjects.length === 0) {
if (objects.length > 0) {
this.dragControls.activate()
}
this._dragObjects = objects
} else {
// 当前拖拽对象不为空时
if (type === 'eq') {
// 是清空拖拽对象的设置,则禁用控制器
if (objects.length === 0) {
this.dragControls.deactivate()
}
this._dragObjects = objects
} else if (type === 'push') {
this._dragObjects.push(...objects)
} else if (type === 'remove') {
this._dragObjects = this._dragObjects.filter((item) => !objects.includes(item))
}
}
this.dragControls.setObjects(this._dragObjects)
}
// 拖拽开始
dragControlsStart(e) {
// 右键拖拽不响应
if (e.e.button === 2 || !e.object?.visible) return
const type = e.object.userData?.t
const entityId = e.object.userData?.entityId
if (!type || !entityId) return
this.currentInteraction = getInteraction(e.object.userData?.t)
if (!this.currentInteraction) {
return
}
// 有效拖拽对象
const enable = this.currentInteraction.dragPointStart(this.viewport, {
object: e.object,
entityId: entityId
})
if (!enable) return
e.e.preventDefault()
// 拖拽时禁用其他控制器
this.viewport.controls.enabled = false
this.isDragging = true
// 记录拖拽按下的位置和对象
this.onDownPosition = { x: e.e.clientX, y: e.e.clientY }
// const itemType: ItemTypeDefineOption = getItemTypeByName(e.object.userData.type)
// if (itemType?.clazz) {
// itemType.clazz.dragPointStart(this.viewport, e.object)
// }
// switch (e.object.userData.type) {
// case Constract.MeasureMarker:
// this.viewport.measure.dragPointStart(e.object)
// break
// }
}
// 拖拽中
drag(e) {
this.currentInteraction?.dragPointMove(this.viewport, e)
}
// 拖拽结束
dragControlsEnd(e) {
// 右键拖拽不响应
if (e.e.button === 2 || !e.object.visible) return
if (!e.object.userData?.t) return
// 单选点击
if (this.onDownPosition.x === e.e.clientX && this.onDownPosition.y === e.e.clientY) {
if (e.object.userData.onClick) {
e.object.userData.onClick(e)
}
}
const ret = this.currentInteraction?.dragPointComplete(this.viewport, e)
if (!ret) return
EventBus.dispatch('selectedObjectPropertyChanged', {})
// 拖拽结束启用其他控制器
this.viewport.controls.enabled = true
this.isDragging = false
// if (e.object.userData?.type) {
// const itemType: ItemTypeDefineOption = getItemTypeByName(e.object.userData.type)
// if (itemType?.clazz) {
// itemType.clazz.dragPointComplete(this.viewport)
// }
// }
// switch (e.object.userData.type) {
// case Constract.MeasureMarker:
// this.viewport.measure.dragPointComplete()
// break
// }
}
// 点击可拖拽物体之外
clickblank(e) {
if (e.e.button === 2) return
}
dispose() {
this._dragObjects = []
this.dragControls.removeEventListener('dragstart', dragStartFn)
this.dragControls.removeEventListener('dragend', dragEndFn)
this.dragControls.dispose()
}
}

25
src/core/controls/SelectInspect.ts

@ -120,8 +120,8 @@ export default class SelectInspect implements IControls {
} }
} }
// 清除之前的红色包围盒线框
clearRedSelectionBoxes() { clearRedSelectionBoxes() {
// 清除之前的红色包围盒线框
if (this.redSelectionGroup.children.length > 0) { if (this.redSelectionGroup.children.length > 0) {
for (const child of this.redSelectionGroup.children) { for (const child of this.redSelectionGroup.children) {
this.redSelectionGroup.remove(child) this.redSelectionGroup.remove(child)
@ -132,16 +132,13 @@ export default class SelectInspect implements IControls {
this.viewport.scene.add(this.redSelectionGroup) this.viewport.scene.add(this.redSelectionGroup)
} }
/** // 创建红选包围盒
*
*/
createRedSelectionBox(object: THREE.Object3D) { createRedSelectionBox(object: THREE.Object3D) {
// 如果对象没有 entityId,则不创建包围盒线框 // 如果对象没有 entityId,则不创建包围盒线框
if (!object.userData.entityId) { if (!object.userData.entityId) {
return return
} }
const box = new THREE.Box3().setFromObject(object) const box = new THREE.Box3().setFromObject(object)
box.expandByScalar(Constract.RED_EXPAND_AMOUNT) // 假设 Constract.RED_EXPAND_AMOUNT 已定义
const min = box.min const min = box.min
const max = box.max const max = box.max
@ -173,9 +170,7 @@ export default class SelectInspect implements IControls {
this.redSelectionGroup.add(selectionBox) this.redSelectionGroup.add(selectionBox)
} }
/** // 更新选中对象的包围盒线框
* 线
*/
updateSelectionBox(selectedObject: THREE.Object3D) { updateSelectionBox(selectedObject: THREE.Object3D) {
this.clearSelectionBox() this.clearSelectionBox()
@ -214,13 +209,6 @@ export default class SelectInspect implements IControls {
const selectionBox = new Line2(lineGeom, this.yellowMaterial) const selectionBox = new Line2(lineGeom, this.yellowMaterial)
selectionBox.computeLineDistances() selectionBox.computeLineDistances()
// 获取包围盒中心并设置位置
const center = new THREE.Vector3()
box.getCenter(center)
// selectionBox.position.copy(center)
selectionBox.computeLineDistances()
this.selectionBox = selectionBox this.selectionBox = selectionBox
console.log('selectedItem', this.viewport.state.selectedItem) console.log('selectedItem', this.viewport.state.selectedItem)
@ -242,6 +230,7 @@ export default class SelectInspect implements IControls {
this.clearRedSelectionBoxes() this.clearRedSelectionBoxes()
} }
// 清除当前选中对象的包围盒线框
clearSelectionBox() { clearSelectionBox() {
if (this.selectionBox) { if (this.selectionBox) {
this.viewport.scene.remove(this.selectionBox) this.viewport.scene.remove(this.selectionBox)
@ -319,7 +308,7 @@ export default class SelectInspect implements IControls {
disposeRect() { disposeRect() {
if (this.rectangle !== null) { if (this.rectangle !== null) {
// 查找在这个矩形内的所有有效业务对象,并将他们添加进 viewport.state.multiSelectedObjects // 查找在这个矩形内的所有有效业务对象,并将他们添加进 viewport.state.multiSelectedObjects
this.multipleSelectedObjects() this.calcRectangleObjectToState()
this.viewport.scene.remove(this.rectangle) this.viewport.scene.remove(this.rectangle)
this.rectangle.geometry.dispose() this.rectangle.geometry.dispose()
this.rectangle = null this.rectangle = null
@ -332,7 +321,7 @@ export default class SelectInspect implements IControls {
this.disposeRect() this.disposeRect()
const clickTime = this.clickTime const clickTime = this.clickTime
this.clickTime = null this.clickTime = null
if (Date.now() - clickTime < 200) { if (Date.now() - clickTime < 100) {
// 如果是点击事件,触发选中逻辑 // 如果是点击事件,触发选中逻辑
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 && objects[0]?.userData?.createType !== 'line') { if (objects.length > 0 && objects[0]?.userData?.entityId && objects[0]?.userData?.createType !== 'line') {
@ -371,7 +360,7 @@ export default class SelectInspect implements IControls {
} }
} }
private multipleSelectedObjects() { private calcRectangleObjectToState() {
if (!this.rectangle || !this.recStartPos) return if (!this.rectangle || !this.recStartPos) return
// 获取矩形的包围盒 // 获取矩形的包围盒

8
src/core/engine/Viewport.ts

@ -14,15 +14,13 @@ import { getAllItemTypes } from '@/model/itemType/ItemTypeDefine.ts'
import SceneHelp from './SceneHelp' import SceneHelp from './SceneHelp'
import SelectInspect from '../controls/SelectInspect' import SelectInspect from '../controls/SelectInspect'
import MouseMoveInspect from '../controls/MouseMoveInspect' import MouseMoveInspect from '../controls/MouseMoveInspect'
import EsDragControls from '../controls/EsDragControls'
import EntityManager from '../manager/EntityManager' import EntityManager from '../manager/EntityManager'
import InteractionManager from '@/core/manager/InteractionManager' import InteractionManager from '@/core/manager/InteractionManager'
import { calcPositionUseSnap } from '@/core/ModelUtils' import { calcPositionUseSnap } from '@/core/ModelUtils'
import StateManager from '@/core/manager/StateManager.ts' import StateManager from '@/core/manager/StateManager.ts'
import EventBus from '@/runtime/EventBus.ts' import EventBus from '@/runtime/EventBus.ts'
import Constract from '@/core/Constract.ts' import Constract from '@/core/Constract.ts'
import type { IMeta } from '@/core/base/IMeta.ts' import DragControl from '@/core/controls/DragControl.ts'
import DragControl2 from '@/core/controls/EsDragControl2.ts'
import type { PropertySetter } from '@/core/base/PropertyTypes.ts' import type { PropertySetter } from '@/core/base/PropertyTypes.ts'
/** /**
@ -41,7 +39,7 @@ export default class Viewport {
scene: SceneHelp scene: SceneHelp
selectInspect = new SelectInspect() selectInspect = new SelectInspect()
mouseMoveInspect = new MouseMoveInspect() mouseMoveInspect = new MouseMoveInspect()
dragControl = new DragControl2() dragControl = new DragControl()
tools: IControls[] = [ tools: IControls[] = [
markRaw(this.selectInspect), markRaw(this.selectInspect),
@ -581,7 +579,7 @@ export interface ViewportState {
multiSelectedObjects: THREE.Object3D[] multiSelectedObjects: THREE.Object3D[]
multiSelectedItems: ItemJson[] multiSelectedItems: ItemJson[]
multiSelectedEntityIds: string[] multiSelectedEntityIds: string[]
multiSelectedObjectMetas: IMeta[] multiSelectedObjectMetas: any[]
view3DMode: string // Constract.Mode2D | Constract.Mode3D view3DMode: string // Constract.Mode2D | Constract.Mode3D

29
src/modules/gstore/GstoreRenderer.ts

@ -31,14 +31,20 @@ export default class GstoreRenderer extends BaseRenderer {
override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, objects: THREE.Object3D[]) { override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, objects: THREE.Object3D[]) {
super.afterCreateOrUpdatePoint(item, option, objects) super.afterCreateOrUpdatePoint(item, option, objects)
const point = objects[0] const group = objects[0]
point.position.y = this.defulePositionY group.position.y = this.defulePositionY
// point.scale.set(item.dt.storeWidth, this.defaultScale.y, item.dt.storeDepth) group.scale.set(item.dt.storeWidth, this.defaultScale.y, item.dt.storeDepth)
point.rotation.set( group.rotation.set(
THREE.MathUtils.degToRad(item.tf[1][0]), THREE.MathUtils.degToRad(item.tf[1][0]),
THREE.MathUtils.degToRad(item.tf[1][1]), THREE.MathUtils.degToRad(item.tf[1][1]),
THREE.MathUtils.degToRad(item.tf[1][2]) THREE.MathUtils.degToRad(item.tf[1][2])
) )
// const planeMesh = group.children[0] as THREE.Mesh
// planeMesh.geometry.dispose()
//
// const newGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth)
// planeMesh.geometry = newGeometry
} }
createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D[] { createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D[] {
@ -56,13 +62,14 @@ export default class GstoreRenderer extends BaseRenderer {
createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] {
// 创建平面几何体 // 创建平面几何体
if (!item.dt.storeWidth || !item.dt.storeDepth) { if (!item.dt.storeWidth || !item.dt.storeDepth) {
system.showErrorDialog('地堆货位缺少 storeWidth 或 storeDepth 属性')
return [] return []
} }
const group = new THREE.Group() const group = new THREE.Group()
group.name = GstoreRenderer.POINT_NAME group.name = GstoreRenderer.POINT_NAME
// 绘制背景矩形框 // 绘制背景矩形框
const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth) const planeGeometry = new THREE.PlaneGeometry(1, 1)
planeGeometry.rotateX(Math.PI / 2) planeGeometry.rotateX(Math.PI / 2)
const planeMaterial = new THREE.MeshBasicMaterial({ const planeMaterial = new THREE.MeshBasicMaterial({
color: '#dee8ee', color: '#dee8ee',
@ -99,13 +106,13 @@ export default class GstoreRenderer extends BaseRenderer {
// 设置位置 // 设置位置
group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2])
const points = [group] const result = [group]
this.fillObjectUserDataFromItem(item, ...points) this.fillObjectUserDataFromItem(item, ...result)
this.afterCreateOrUpdatePoint(item, option, points) this.afterCreateOrUpdatePoint(item, option, result)
this.tempViewport.entityManager.appendObject(item.id, points) this.tempViewport.entityManager.appendObject(item.id, result)
this.appendToScene(...points) this.appendToScene(...result)
return points return result
} }
dispose() { dispose() {

Loading…
Cancel
Save