diff --git a/src/core/base/BaseRenderer.ts b/src/core/base/BaseRenderer.ts index 88056a1..1fef5cd 100644 --- a/src/core/base/BaseRenderer.ts +++ b/src/core/base/BaseRenderer.ts @@ -124,7 +124,7 @@ export default abstract class BaseRenderer { this.tempViewport.scene.add(...objects) const dragObjects = objects.filter(obj => !!obj.userData.draggable) - this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') + //this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') } removeFromScene(...objects: THREE.Object3D[]) { @@ -133,7 +133,7 @@ export default abstract class BaseRenderer { return } this.tempViewport.scene.remove(...objects) - this.tempViewport.dragControl.setDragObjects(objects, 'remove') + //this.tempViewport.dragControl.setDragObjects(objects, 'remove') } /** diff --git a/src/core/controls/EsDragControl2.ts b/src/core/controls/EsDragControl2.ts new file mode 100644 index 0000000..b69ea09 --- /dev/null +++ b/src/core/controls/EsDragControl2.ts @@ -0,0 +1,265 @@ +import * as THREE from 'three' +import type Viewport from '@/core/engine/Viewport.ts' +import type IControls from '@/core/controls/IControls.ts' +import { Curve } from 'three' +import { getClosestObject } from '@/core/ModelUtils.ts' +import EventBus from '@/runtime/EventBus.ts' + +/** + * DragControl2 - ThreeJS 拖拽管理器(仅限 X/Z 平面) + */ +export default class DragControl2 implements IControls { + private viewport: Viewport + private enabled: boolean = true + + private isDragging: boolean = false + private isPointerDown: boolean = false + private dragStartMouse: THREE.Vector2 = new THREE.Vector2() + + private dragShadows: THREE.Group | null = null + + init(viewport: Viewport): void { + this.viewport = viewport + + const domElement = this.viewport.renderer.domElement + domElement.addEventListener('pointermove', this.onPointerMove) + domElement.addEventListener('pointerdown', this.onPointerDown) + domElement.addEventListener('pointerup', this.onPointerUp) + domElement.addEventListener('pointerleave', this.onPointerLeave) + + domElement.style.cursor = 'auto' + } + + + /** + * 设置启用/禁用 + */ + set enable(value: boolean) { + this.enabled = value + if (!value) { + this.cleanupDrag() + } + } + + get enable(): boolean { + return this.enabled + } + + /** + * 卸载资源 + */ + dispose(): void { + const domElement = this.viewport.renderer.domElement + domElement.removeEventListener('pointermove', this.onPointerMove) + domElement.removeEventListener('pointerdown', this.onPointerDown) + domElement.removeEventListener('pointerup', this.onPointerUp) + domElement.removeEventListener('pointerleave', this.onPointerLeave) + + this.cleanupDrag() + domElement.style.cursor = 'auto' + } + + /** + * 清理拖拽状态 + */ + private cleanupDrag(): void { + if (this.isDragging || this.isPointerDown) { + this.isDragging = false + this.isPointerDown = false + this.dragStartMouse.set(NaN, NaN) + this.removeShadows() + } + } + + /** + * 获取当前鼠标坐标(归一化设备坐标) + */ + private getMousePosition(clientX: number, clientY: number): THREE.Vector2 { + const rect = this.viewport.renderer.domElement.getBoundingClientRect() + return new THREE.Vector2( + ((clientX - rect.left) / rect.width) * 2 - 1, + ((clientY - rect.top) / rect.height) * -2 + 1 + ) + } + + /** + * 射线检测,返回第一个可拖拽对象 + */ + private getIntersectedDraggableObject(mouse: THREE.Vector2): THREE.Object3D | null { + const raycaster = new THREE.Raycaster() + raycaster.setFromCamera(mouse, this.viewport.camera) + + const draggableObjects = this.viewport.entityManager._draggableObjects || [] + const intersects = raycaster.intersectObjects(draggableObjects, true) + + if (intersects.length > 0) { + return getClosestObject(intersects[0].object) + } + } + + /** + * 创建拖拽阴影 + */ + private createShadows(objects: THREE.Object3D[]): void { + this.removeShadows() + + console.log('createShadows', objects.map(obj => obj.name)) + + this.dragShadows = new THREE.Group() + for (const obj of objects) { + if (!obj.userData.entityId) { + console.error('Object does not have entityId:', obj.name) + continue // 跳过没有 entityId 的对象 + } + + const shadow = obj.clone() + + shadow.userData = { + isShadow: true, + entityId: obj.userData.entityId, + originPosition: obj.position.clone() // 保存原始位置 + } + this.dragShadows.add(shadow) + } + this.viewport.scene.add(this.dragShadows) + } + + /** + * 移除阴影 + */ + private removeShadows(): void { + if (this.dragShadows) { + this.viewport.scene.remove(this.dragShadows) + this.dragShadows = null + } + } + + /** + * 更新阴影位置(仅 X/Z 平面) + */ + private updateShadows(newPosition: THREE.Vector2): void { + if (!this.dragShadows) return + + // 计算新位置与拖拽开始位置的偏移量 + const offsetX = newPosition.x - this.dragStartMouse.x + const offsetZ = newPosition.y - this.dragStartMouse.y + + this.dragShadows.children.forEach((shadow, index) => { + const newPosX = shadow.userData.originPosition.x + offsetX + const newPosZ = shadow.userData.originPosition.z + offsetZ + + shadow.position.set(newPosX, shadow.userData.originPosition.y, newPosZ) // 锁定 Y 轴高度 + console.log(`Updated shadow position for ${shadow.name}:`, shadow.position) + }) + } + + private dragComplete = (startPos: THREE.Vector2, targetPos: THREE.Vector2): void => { + // console.log(`Drag completed from ${startPos.toArray()} to ${targetPos.toArray()}`) + this.viewport.stateManager.beginStateUpdate() + for (const object of this.dragShadows.children) { + const entityId = object.userData.entityId + if (entityId) { + const entity = this.viewport.stateManager.findItemById(entityId) + if (entity) { + // 更新实体位置 + entity.tf[0][0] = object.position.x + entity.tf[0][2] = object.position.z + + } else { + system.showErrorDialog('not found entityId:' + entityId) + } + } else { + system.showErrorDialog('not found entity') + } + } + this.viewport.stateManager.endStateUpdate() + EventBus.dispatch('multiselectedObjectChanged', { + multiSelectedObjects: this.viewport.state.multiSelectedObjects + }) + EventBus.dispatch('selectedObjectPropertyChanged', {}) + + this.cleanupDrag() + } + + /** + * pointermove 事件处理 + */ + private onPointerMove = (event: PointerEvent): void => { + if (!this.enabled) return + + if (!isNaN(this.dragStartMouse.x) && !isNaN(this.dragStartMouse.y) && this.dragShadows) { + this.isDragging = true + // 更新鼠标样式 + this.viewport.renderer.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.viewport.renderer.domElement.style.cursor = 'grab' + } else { + this.viewport.renderer.domElement.style.cursor = 'auto' + } + } + } + + /** + * 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.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 => { + if (!this.enabled) return + + this.dragStartMouse.set(NaN, NaN) + 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) + } + } + } + + /** + * pointerleave 事件处理 + */ + private onPointerLeave = (_event: PointerEvent): void => { + if (!this.enabled) return + this.cleanupDrag() + } +} diff --git a/src/core/engine/Viewport.ts b/src/core/engine/Viewport.ts index 8928f32..58daa86 100644 --- a/src/core/engine/Viewport.ts +++ b/src/core/engine/Viewport.ts @@ -22,6 +22,7 @@ import StateManager from '@/core/manager/StateManager.ts' import EventBus from '@/runtime/EventBus.ts' import Constract from '@/core/Constract.ts' import type { IMeta } from '@/core/base/IMeta.ts' +import DragControl2 from '@/core/controls/EsDragControl2.ts' /** * 视窗对象 @@ -34,15 +35,17 @@ export default class Viewport { statsControls: Stats controls: OrbitControls raycaster: THREE.Raycaster - dragControl: any // EsDragControls + // dragControl: any // EsDragControls animationFrameId: any = null scene: SceneHelp selectInspect = new SelectInspect() mouseMoveInspect = new MouseMoveInspect() + dragControl = new DragControl2() tools: IControls[] = [ markRaw(this.selectInspect), - markRaw(this.mouseMoveInspect) + markRaw(this.mouseMoveInspect), + markRaw(this.dragControl) ] // 状态管理器 @@ -155,18 +158,9 @@ export default class Viewport { this.renderer = renderer - // 创建正交摄像机 - // this.initMode2DCamera() - this.watchList.push(watch(() => this.state.view3DMode, (newVal) => { - if (newVal === Constract.Mode3D) { - this.initMode3DCamera() - } else { - this.initMode2DCamera() - } - }, { immediate: true })) // 注册拖拽组件 - this.dragControl = new EsDragControls(this) + // this.dragControl = new EsDragControls(this) // 性能监控 const statsControls = new Stats() @@ -202,6 +196,16 @@ export default class Viewport { tool.init(this) } + // 创建正交摄像机 + // this.initMode2DCamera() + this.watchList.push(watch(() => this.state.view3DMode, (newVal) => { + if (newVal === Constract.Mode3D) { + this.initMode3DCamera() + } else { + this.initMode2DCamera() + } + }, { immediate: true })) + // 触发所有物品类型的 afterAddViewport 方法 _.forEach(getAllItemTypes(), (itemType: ItemTypeDefineOption) => { itemType.clazz.afterAddViewport(this) diff --git a/src/model/itemType/ItemType.ts b/src/model/itemType/ItemType.ts index ed1eb1a..220e07b 100644 --- a/src/model/itemType/ItemType.ts +++ b/src/model/itemType/ItemType.ts @@ -60,7 +60,7 @@ export default abstract class ItemType { * 添加到 viewport 后的回调 */ afterAddViewport(viewport: Viewport): void { - viewport.dragControl.setDragObjects(this.pointArray, 'push') + //viewport.dragControl.setDragObjects(this.pointArray, 'push') const toolbox = this.createToolbox(viewport) viewport.toolbox[this.name] = toolbox } @@ -74,4 +74,4 @@ export default abstract class ItemType { * EsDragControls 拖拽完成(放开鼠标)时的回调 */ abstract dragPointComplete(viewport: Viewport) -} \ No newline at end of file +} diff --git a/src/model/itemType/Toolbox.ts b/src/model/itemType/Toolbox.ts index caae638..fd9845e 100644 --- a/src/model/itemType/Toolbox.ts +++ b/src/model/itemType/Toolbox.ts @@ -290,7 +290,7 @@ export default abstract class Toolbox { this.addToScene(marker) // 把点加入拖拽控制器 - this.viewport.dragControl.setDragObjects([marker], 'push') + //this.viewport.dragControl.setDragObjects([marker], 'push') if (this.startPoint) { this.afterAddPoint(this.startPoint, marker) @@ -320,4 +320,4 @@ export default abstract class Toolbox { */ destory() { } -} \ No newline at end of file +} diff --git a/src/modules/gstore/GstoreRenderer.ts b/src/modules/gstore/GstoreRenderer.ts index 2f7ea83..d4265ed 100644 --- a/src/modules/gstore/GstoreRenderer.ts +++ b/src/modules/gstore/GstoreRenderer.ts @@ -41,7 +41,6 @@ export default class GstoreRenderer extends BaseRenderer { ) } - createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D[] { throw new Error('not allow store line.') } @@ -50,6 +49,10 @@ export default class GstoreRenderer extends BaseRenderer { throw new Error('not allow store line.') } + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { + throw new Error('not allow createPointBasic.') + } + createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { // 创建平面几何体 if (!item.dt.storeWidth || !item.dt.storeDepth) { @@ -59,7 +62,7 @@ export default class GstoreRenderer extends BaseRenderer { group.name = GstoreRenderer.POINT_NAME // 绘制背景矩形框 - const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth); + const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth) planeGeometry.rotateX(Math.PI / 2) const planeMaterial = new THREE.MeshBasicMaterial({ color: '#dee8ee', @@ -67,8 +70,8 @@ export default class GstoreRenderer extends BaseRenderer { opacity: 0.5, // 50%透明度 depthWrite: false, // 防止深度冲突 side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"} - }); - const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial); + }) + const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial) group.add(planeMesh) // 绘制边框 @@ -76,30 +79,32 @@ export default class GstoreRenderer extends BaseRenderer { const lineYLen = item.dt.storeDepth - this.defaultLineWidth const lineGeometry = new LineGeometry().setPositions([ - -(lineXLen/2),0,-(lineYLen/2), - lineXLen/2,0,-(lineYLen/2), - lineXLen/2,0,lineYLen/2, - -(lineXLen/2),0,lineYLen/2, - -(lineXLen/2),0,-(lineYLen/2) - ]); + -(lineXLen / 2), 0, -(lineYLen / 2), + lineXLen / 2, 0, -(lineYLen / 2), + lineXLen / 2, 0, lineYLen / 2, + -(lineXLen / 2), 0, lineYLen / 2, + -(lineXLen / 2), 0, -(lineYLen / 2) + ]) const lineMaterial = new LineMaterial({ color: '#038217', linewidth: this.defaultLineWidth, worldUnits: true, resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), side: THREE.DoubleSide - }); + }) // - const line = new Line2(lineGeometry, lineMaterial); + const line = new Line2(lineGeometry, lineMaterial) group.add(line as THREE.Object3D) // 设置位置 group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + const points = [group] this.fillObjectUserDataFromItem(item, ...points) this.afterCreateOrUpdatePoint(item, option, points) this.tempViewport.entityManager.appendObject(item.id, points) this.appendToScene(...points) + return points } diff --git a/src/modules/measure/MeasureRenderer.ts b/src/modules/measure/MeasureRenderer.ts index b38683e..b591705 100644 --- a/src/modules/measure/MeasureRenderer.ts +++ b/src/modules/measure/MeasureRenderer.ts @@ -110,7 +110,7 @@ export default class MeasureRenderer extends BaseRenderer { } const dragObjects = objects.filter(obj => !!obj.userData.draggable) - this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') + //this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') this.group.add(...objects) } diff --git a/src/modules/way/WayRenderer.ts b/src/modules/way/WayRenderer.ts index 19caaf9..6a13268 100644 --- a/src/modules/way/WayRenderer.ts +++ b/src/modules/way/WayRenderer.ts @@ -177,7 +177,7 @@ export default class WayRenderer extends BaseRenderer { appendToScene(...objects: THREE.Object3D[]) { const dragObjects = objects.filter(obj => !!obj.userData.draggable) - this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') + //this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') // this.tempViewport.dragControl.setDragObjects(objects, 'remove') this.tempViewport?.scene.add(...objects)