669 lines
25 KiB
TypeScript
669 lines
25 KiB
TypeScript
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
|
||
import { PointerDragBehavior } from '@babylonjs/core/Behaviors/Meshes/pointerDragBehavior';
|
||
import { Vector3 } from '@babylonjs/core/Maths/math.vector';
|
||
import { Scene } from '@babylonjs/core/scene';
|
||
import { Monobehiver } from '../base/Monobehiver';
|
||
|
||
/**
|
||
* 拖拽配置接口
|
||
*/
|
||
export interface DragConfig {
|
||
enable: boolean;
|
||
axis?: 'x' | 'y' | 'z' | 'xy' | 'xz' | 'yz' | 'xyz';
|
||
step?: number;
|
||
snapToZone?: boolean; // 拖拽吸附:松开时自动吸附到最近的分割区域
|
||
returnWhenOutOfBounds?: boolean; // 拖拽到区域外时返回原位置
|
||
handleOccupiedZone?: boolean; // 拖拽到已占用区域时的处理:true=返回原位置或替换,false=允许重叠
|
||
occupiedZoneAction?: 'return' | 'replace'; // 当 handleOccupiedZone=true 时的具体行为:'return' 返回原位置,'replace' 替换目标位置的模型
|
||
}
|
||
|
||
/**
|
||
* 模型拖拽信息
|
||
*/
|
||
interface ModelDragInfo {
|
||
config: DragConfig;
|
||
behavior: PointerDragBehavior | null;
|
||
currentAxis: 'x' | 'y' | 'z' | null;
|
||
}
|
||
|
||
/**
|
||
* 模型拖拽管理器 - 负责处理模型的拖拽交互
|
||
*/
|
||
export class AppModelDrag extends Monobehiver {
|
||
private modelDragMap: Map<string, ModelDragInfo>;
|
||
private scene: Scene | null;
|
||
|
||
constructor(mainApp: any) {
|
||
super(mainApp);
|
||
this.modelDragMap = new Map();
|
||
this.scene = null;
|
||
}
|
||
|
||
/**
|
||
* 初始化拖拽管理器
|
||
*/
|
||
Awake(): void {
|
||
this.scene = this.mainApp.appScene.object;
|
||
if (!this.scene) {
|
||
console.warn('Scene not initialized');
|
||
return;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为模型配置拖拽
|
||
* @param modelId 模型ID
|
||
* @param config 拖拽配置
|
||
*/
|
||
configureDrag(modelId: string, config: DragConfig): void {
|
||
// 获取模型的根网格
|
||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||
if (!meshes || !meshes.length) {
|
||
console.warn(`Model not found: ${modelId}`);
|
||
return;
|
||
}
|
||
|
||
const rootMesh = meshes[0]; // 第一个是根节点
|
||
|
||
// 如果已存在,先移除旧的行为
|
||
const existingInfo = this.modelDragMap.get(modelId);
|
||
if (existingInfo?.behavior) {
|
||
rootMesh.removeBehavior(existingInfo.behavior);
|
||
}
|
||
|
||
// 创建拖拽信息
|
||
const dragInfo: ModelDragInfo = {
|
||
config: { ...config },
|
||
behavior: null,
|
||
currentAxis: this.getFirstAvailableAxis(config.axis || 'xyz')
|
||
};
|
||
|
||
if (config.enable) {
|
||
// 创建并配置拖拽行为
|
||
dragInfo.behavior = this.createDragBehavior(modelId, dragInfo);
|
||
rootMesh.addBehavior(dragInfo.behavior);
|
||
}
|
||
|
||
this.modelDragMap.set(modelId, dragInfo);
|
||
}
|
||
|
||
/**
|
||
* 创建拖拽行为
|
||
*/
|
||
private createDragBehavior(modelId: string, dragInfo: ModelDragInfo): PointerDragBehavior {
|
||
const axis = dragInfo.currentAxis;
|
||
let dragAxis: Vector3;
|
||
|
||
// 根据当前激活的轴创建拖拽向量
|
||
switch (axis) {
|
||
case 'x':
|
||
dragAxis = new Vector3(1, 0, 0);
|
||
break;
|
||
case 'y':
|
||
dragAxis = new Vector3(0, 1, 0);
|
||
break;
|
||
case 'z':
|
||
dragAxis = new Vector3(0, 0, 1);
|
||
break;
|
||
default:
|
||
dragAxis = new Vector3(1, 0, 0);
|
||
}
|
||
|
||
// 创建拖拽行为
|
||
const pointerDragBehavior = new PointerDragBehavior({ dragAxis: dragAxis });
|
||
|
||
// 使用世界坐标系而不是物体本地坐标系
|
||
pointerDragBehavior.useObjectOrientationForDragging = false;
|
||
|
||
// 记录拖拽起始位置和状态
|
||
let dragStartPosition: Vector3 | null = null;
|
||
let hasShownZones = false; // 是否已显示分割区域
|
||
|
||
// 监听拖拽开始事件
|
||
pointerDragBehavior.onDragStartObservable.add(() => {
|
||
// 记录起始位置
|
||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||
if (meshes && meshes.length > 0) {
|
||
dragStartPosition = meshes[0].position.clone();
|
||
}
|
||
|
||
// 禁用相机控制
|
||
this.disableCameraControl();
|
||
|
||
// 不在这里显示分割区域,等到实际拖动时再显示
|
||
hasShownZones = false;
|
||
});
|
||
|
||
// 监听拖拽中事件(用于延迟显示分割区域)
|
||
pointerDragBehavior.onDragObservable.add((event) => {
|
||
// 检查是否实际移动了
|
||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||
if (meshes && meshes.length > 0 && dragStartPosition) {
|
||
const distance = Vector3.Distance(dragStartPosition, meshes[0].position);
|
||
|
||
// 如果移动距离超过阈值且还没显示分割区域,则显示
|
||
if (distance > 0.01 && !hasShownZones && dragInfo.config.snapToZone) {
|
||
this.showZonesForModel(modelId);
|
||
hasShownZones = true;
|
||
}
|
||
}
|
||
});
|
||
|
||
// 监听拖拽结束事件
|
||
pointerDragBehavior.onDragEndObservable.add(() => {
|
||
// 检查是否实际移动了
|
||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||
let hasMoved = false;
|
||
if (meshes && meshes.length > 0 && dragStartPosition) {
|
||
const distance = Vector3.Distance(dragStartPosition, meshes[0].position);
|
||
hasMoved = distance > 0.01; // 移动距离大于 0.01 才算拖拽
|
||
}
|
||
|
||
// 恢复相机控制
|
||
this.enableCameraControl();
|
||
|
||
// 只有在实际移动的情况下才执行拖拽逻辑
|
||
if (hasMoved) {
|
||
// 如果启用了拖拽吸附,隐藏分割区域并吸附到最近区域
|
||
if (dragInfo.config.snapToZone && hasShownZones) {
|
||
this.hideZonesForModel(modelId);
|
||
this.snapModelToZone(modelId);
|
||
} else {
|
||
// 否则只更新映射关系
|
||
this.updateModelZoneMapping(modelId);
|
||
}
|
||
}
|
||
|
||
// 清除状态
|
||
dragStartPosition = null;
|
||
hasShownZones = false;
|
||
});
|
||
|
||
return pointerDragBehavior;
|
||
}
|
||
|
||
/**
|
||
* 获取模型的拖拽配置
|
||
* @param modelId 模型ID
|
||
*/
|
||
getDragConfig(modelId: string): DragConfig | undefined {
|
||
return this.modelDragMap.get(modelId)?.config;
|
||
}
|
||
|
||
/**
|
||
* 启用/禁用模型拖拽
|
||
* @param modelId 模型ID
|
||
* @param enable 是否启用
|
||
*/
|
||
setDragEnabled(modelId: string, enable: boolean): void {
|
||
const dragInfo = this.modelDragMap.get(modelId);
|
||
if (!dragInfo) return;
|
||
|
||
dragInfo.config.enable = enable;
|
||
|
||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||
if (!meshes || !meshes.length) return;
|
||
|
||
const rootMesh = meshes[0];
|
||
|
||
if (enable) {
|
||
// 启用:创建并添加行为
|
||
if (!dragInfo.behavior) {
|
||
dragInfo.behavior = this.createDragBehavior(modelId, dragInfo);
|
||
rootMesh.addBehavior(dragInfo.behavior);
|
||
}
|
||
} else {
|
||
// 禁用:移除行为
|
||
if (dragInfo.behavior) {
|
||
rootMesh.removeBehavior(dragInfo.behavior);
|
||
dragInfo.behavior = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 切换激活的轴向
|
||
* @param modelId 模型ID
|
||
* @param axis 要激活的轴向
|
||
*/
|
||
switchAxis(modelId: string, axis: 'x' | 'y' | 'z'): void {
|
||
const dragInfo = this.modelDragMap.get(modelId);
|
||
if (!dragInfo) return;
|
||
|
||
// 检查该轴是否在允许的轴向中
|
||
if (!this.isAxisAllowed(axis, dragInfo.config.axis || 'xyz')) {
|
||
console.warn(`Axis ${axis} is not allowed for model ${modelId}`);
|
||
return;
|
||
}
|
||
|
||
// 更新当前轴
|
||
dragInfo.currentAxis = axis;
|
||
|
||
// 重新创建拖拽行为
|
||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||
if (!meshes || !meshes.length) return;
|
||
|
||
const rootMesh = meshes[0];
|
||
|
||
// 移除旧行为
|
||
if (dragInfo.behavior) {
|
||
rootMesh.removeBehavior(dragInfo.behavior);
|
||
}
|
||
|
||
// 创建新行为
|
||
if (dragInfo.config.enable) {
|
||
dragInfo.behavior = this.createDragBehavior(modelId, dragInfo);
|
||
rootMesh.addBehavior(dragInfo.behavior);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取配置中的第一个可用轴
|
||
*/
|
||
private getFirstAvailableAxis(axisConfig: string): 'x' | 'y' | 'z' | null {
|
||
if (axisConfig.includes('x')) return 'x';
|
||
if (axisConfig.includes('y')) return 'y';
|
||
if (axisConfig.includes('z')) return 'z';
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 检查轴是否在允许的配置中
|
||
*/
|
||
private isAxisAllowed(axis: 'x' | 'y' | 'z', axisConfig: string): boolean {
|
||
return axisConfig.includes(axis);
|
||
}
|
||
|
||
/**
|
||
* 禁用相机控制
|
||
*/
|
||
private disableCameraControl(): void {
|
||
const camera = this.mainApp.appCamera?.object;
|
||
if (camera) {
|
||
camera.detachControl();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 启用相机控制
|
||
*/
|
||
private enableCameraControl(): void {
|
||
const camera = this.mainApp.appCamera?.object;
|
||
const canvas = this.mainApp.appEngin?.object?.getRenderingCanvas();
|
||
if (camera && canvas) {
|
||
camera.attachControl(canvas, true);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示模型所在墙面的分割区域
|
||
* @param modelId 模型ID
|
||
*/
|
||
private showZonesForModel(modelId: string): void {
|
||
const appDropZone = this.mainApp.appDropZone;
|
||
if (!appDropZone) return;
|
||
|
||
// 查找模型所在的墙面
|
||
let wallName: string | null = null;
|
||
appDropZone['zoneModelMap']?.forEach((id: string, zoneKey: string) => {
|
||
if (id === modelId) {
|
||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
||
if (match) {
|
||
wallName = match[1];
|
||
}
|
||
}
|
||
});
|
||
|
||
if (wallName) {
|
||
console.log(`[拖拽吸附] 显示墙面 ${wallName} 的分割区域`);
|
||
// 只显示该墙面的分割区域
|
||
appDropZone.showWall(wallName);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 隐藏分割区域
|
||
* @param modelId 模型ID
|
||
*/
|
||
private hideZonesForModel(modelId: string): void {
|
||
const appDropZone = this.mainApp.appDropZone;
|
||
if (!appDropZone) return;
|
||
|
||
console.log(`[拖拽吸附] 隐藏分割区域`);
|
||
appDropZone.hide();
|
||
}
|
||
|
||
/**
|
||
* 将模型吸附到最近的分割区域
|
||
* @param modelId 模型ID
|
||
*/
|
||
private snapModelToZone(modelId: string): void {
|
||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||
if (!meshes || !meshes.length) return;
|
||
|
||
const rootMesh = meshes[0];
|
||
const appDropZone = this.mainApp.appDropZone;
|
||
if (!appDropZone) return;
|
||
|
||
// 查找模型原来所在的墙面和区域索引
|
||
let wallName: string | null = null;
|
||
let originalZoneIndex: number = -1;
|
||
appDropZone['zoneModelMap']?.forEach((id: string, zoneKey: string) => {
|
||
if (id === modelId) {
|
||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
||
if (match) {
|
||
wallName = match[1];
|
||
originalZoneIndex = parseInt(match[2]);
|
||
}
|
||
}
|
||
});
|
||
|
||
if (!wallName) return;
|
||
|
||
// 获取该墙面的所有分割区域
|
||
const wallZones = appDropZone.getZonesByWall(wallName);
|
||
if (!wallZones.length) return;
|
||
|
||
// 找到最近的区域
|
||
let closestZoneIndex = -1;
|
||
let minDistance = Number.POSITIVE_INFINITY;
|
||
|
||
wallZones.forEach((zone, index) => {
|
||
const distance = rootMesh.position.subtract(zone.center).length();
|
||
if (distance < minDistance) {
|
||
minDistance = distance;
|
||
closestZoneIndex = index;
|
||
}
|
||
});
|
||
|
||
if (closestZoneIndex === -1) return;
|
||
|
||
// 获取拖拽配置
|
||
const dragInfo = this.modelDragMap.get(modelId);
|
||
const returnWhenOutOfBounds = dragInfo?.config.returnWhenOutOfBounds ?? false;
|
||
const handleOccupiedZone = dragInfo?.config.handleOccupiedZone ?? false;
|
||
const occupiedZoneAction = dragInfo?.config.occupiedZoneAction ?? 'return';
|
||
|
||
// 检查是否拖拽到区域外
|
||
let isOutOfBounds = false;
|
||
|
||
// 计算墙面的边界
|
||
let minX = Number.POSITIVE_INFINITY;
|
||
let maxX = Number.NEGATIVE_INFINITY;
|
||
let minZ = Number.POSITIVE_INFINITY;
|
||
let maxZ = Number.NEGATIVE_INFINITY;
|
||
|
||
wallZones.forEach(zone => {
|
||
const halfWidth = zone.width / 2;
|
||
|
||
// 根据法线方向计算边界
|
||
if (Math.abs(zone.normal.x) > 0.5) {
|
||
// 左右墙面(法线沿X轴)
|
||
minZ = Math.min(minZ, zone.center.z - halfWidth);
|
||
maxZ = Math.max(maxZ, zone.center.z + halfWidth);
|
||
} else if (Math.abs(zone.normal.z) > 0.5) {
|
||
// 前后墙面(法线沿Z轴)
|
||
minX = Math.min(minX, zone.center.x - halfWidth);
|
||
maxX = Math.max(maxX, zone.center.x + halfWidth);
|
||
}
|
||
});
|
||
|
||
// 检查当前位置是否在边界内
|
||
const currentPos = rootMesh.position;
|
||
|
||
if (minX !== Number.POSITIVE_INFINITY && maxX !== Number.NEGATIVE_INFINITY) {
|
||
if (currentPos.x < minX || currentPos.x > maxX) {
|
||
isOutOfBounds = true;
|
||
}
|
||
}
|
||
if (minZ !== Number.POSITIVE_INFINITY && maxZ !== Number.NEGATIVE_INFINITY) {
|
||
if (currentPos.z < minZ || currentPos.z > maxZ) {
|
||
isOutOfBounds = true;
|
||
}
|
||
}
|
||
|
||
// 处理超出边界的情况(开关2:returnWhenOutOfBounds)
|
||
if (isOutOfBounds) {
|
||
console.log(`[拖拽吸附] 模型 ${modelId} 超出边界`);
|
||
|
||
if (returnWhenOutOfBounds) {
|
||
// 启用了边界返回,回到原来的区域
|
||
console.log(`[拖拽吸附] 启用边界返回,回到原区域 ${originalZoneIndex}`);
|
||
if (originalZoneIndex !== -1) {
|
||
const originalZone = wallZones[originalZoneIndex];
|
||
if (originalZone) {
|
||
const offsetDistance = 0;
|
||
const returnPosition = originalZone.center.add(originalZone.normal.scale(offsetDistance));
|
||
rootMesh.position.copyFrom(returnPosition);
|
||
|
||
const targetDirection = originalZone.normal.scale(-1);
|
||
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||
rootMesh.rotation.y = angle;
|
||
|
||
console.log(`[拖拽吸附] 模型 ${modelId} 已返回原区域 ${originalZoneIndex}`);
|
||
return; // 不更新映射,保持原映射
|
||
}
|
||
}
|
||
} else {
|
||
// 未启用边界返回,保持当前位置,不做吸附
|
||
console.log(`[拖拽吸附] 未启用边界返回,保持当前位置,不做吸附`);
|
||
// 更新映射关系(可能移出了原区域)
|
||
this.updateModelZoneMapping(modelId);
|
||
return;
|
||
}
|
||
}
|
||
|
||
const targetZone = wallZones[closestZoneIndex];
|
||
|
||
// 检查目标区域是否已被其他模型占用(开关2:handleOccupiedZone)
|
||
const targetZoneKey = `${wallName}[${closestZoneIndex}]`;
|
||
const occupyingModelId = appDropZone['zoneModelMap']?.get(targetZoneKey);
|
||
|
||
if (occupyingModelId && occupyingModelId !== modelId) {
|
||
// 目标区域已被其他模型占用
|
||
console.log(`[拖拽吸附] 目标区域 ${closestZoneIndex} 已被模型 ${occupyingModelId} 占用`);
|
||
|
||
if (handleOccupiedZone) {
|
||
// 启用了占用区域处理
|
||
if (occupiedZoneAction === 'return') {
|
||
// 返回原位置
|
||
console.log(`[拖拽吸附] 配置为返回原位置,回到区域 ${originalZoneIndex}`);
|
||
if (originalZoneIndex !== -1) {
|
||
const originalZone = wallZones[originalZoneIndex];
|
||
if (originalZone) {
|
||
const offsetDistance = 0;
|
||
const returnPosition = originalZone.center.add(originalZone.normal.scale(offsetDistance));
|
||
rootMesh.position.copyFrom(returnPosition);
|
||
|
||
const targetDirection = originalZone.normal.scale(-1);
|
||
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||
rootMesh.rotation.y = angle;
|
||
|
||
console.log(`[拖拽吸附] 模型 ${modelId} 返回原区域 ${originalZoneIndex}`);
|
||
return; // 不更新映射,保持原映射
|
||
}
|
||
}
|
||
} else if (occupiedZoneAction === 'replace') {
|
||
// 替换目标位置的模型(继续执行后面的逻辑)
|
||
console.log(`[拖拽吸附] 配置为替换模型,将替换模型 ${occupyingModelId}`);
|
||
}
|
||
} else {
|
||
// 未启用占用区域处理,允许重叠(继续执行后面的逻辑)
|
||
console.log(`[拖拽吸附] 未启用占用区域处理,允许重叠`);
|
||
}
|
||
}
|
||
|
||
// 计算吸附位置(区域中心 + 法线偏移)
|
||
const offsetDistance = 0;
|
||
const snapPosition = targetZone.center.add(targetZone.normal.scale(offsetDistance));
|
||
|
||
// 吸附到目标位置
|
||
rootMesh.position.copyFrom(snapPosition);
|
||
|
||
// 更新旋转
|
||
const targetDirection = targetZone.normal.scale(-1);
|
||
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||
rootMesh.rotation.y = angle;
|
||
|
||
console.log(`[拖拽吸附] 模型 ${modelId} 吸附到区域 ${closestZoneIndex}`);
|
||
|
||
// 更新映射关系
|
||
this.updateModelZoneMapping(modelId);
|
||
}
|
||
|
||
/**
|
||
* 更新模型所属的分割区域映射
|
||
* @param modelId 模型ID
|
||
*/
|
||
private updateModelZoneMapping(modelId: string): void {
|
||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||
if (!meshes || !meshes.length) return;
|
||
|
||
const rootMesh = meshes[0];
|
||
const modelPosition = rootMesh.position;
|
||
|
||
console.log(`[边界检测] 模型 ${modelId} 拖拽结束,当前位置:`, modelPosition);
|
||
|
||
// 获取 AppDropZone
|
||
const appDropZone = this.mainApp.appDropZone;
|
||
if (!appDropZone) return;
|
||
|
||
// 查找该模型原本所在的墙面
|
||
let originalWallName: string | null = null;
|
||
appDropZone['zoneModelMap']?.forEach((id: string, zoneKey: string) => {
|
||
if (id === modelId) {
|
||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
||
if (match) {
|
||
originalWallName = match[1];
|
||
}
|
||
}
|
||
});
|
||
|
||
if (!originalWallName) {
|
||
console.log(`[边界检测] 模型 ${modelId} 未找到原始墙面,跳过检测`);
|
||
return;
|
||
}
|
||
|
||
console.log(`[边界检测] 模型 ${modelId} 原始墙面: ${originalWallName}`);
|
||
|
||
// 获取该墙面的所有分割区域
|
||
const wallZones = appDropZone.getZonesByWall(originalWallName);
|
||
if (!wallZones.length) return;
|
||
|
||
console.log(`[边界检测] 墙面 ${originalWallName} 有 ${wallZones.length} 个分割区域`);
|
||
|
||
// 计算模型与每个分割区域的距离,找到最近的区域
|
||
let closestZoneIndex = -1;
|
||
let minDistance = Number.POSITIVE_INFINITY;
|
||
|
||
wallZones.forEach((zone, index) => {
|
||
// 计算模型位置到区域中心的距离
|
||
const distance = modelPosition.subtract(zone.center).length();
|
||
console.log(`[边界检测] 区域 ${index} 中心:`, zone.center, `距离: ${distance.toFixed(3)}`);
|
||
|
||
if (distance < minDistance) {
|
||
minDistance = distance;
|
||
closestZoneIndex = index;
|
||
}
|
||
});
|
||
|
||
if (closestZoneIndex === -1) {
|
||
console.log(`[边界检测] 未找到最近的区域`);
|
||
return;
|
||
}
|
||
|
||
console.log(`[边界检测] 模型 ${modelId} 最接近区域 ${closestZoneIndex},距离: ${minDistance.toFixed(3)}`);
|
||
|
||
// 查找模型当前所在的区域索引
|
||
let currentZoneIndex = -1;
|
||
appDropZone['zoneModelMap']?.forEach((id: string, zoneKey: string) => {
|
||
if (id === modelId) {
|
||
const match = zoneKey.match(/^.+\[(\d+)\]$/);
|
||
if (match) {
|
||
currentZoneIndex = parseInt(match[1]);
|
||
}
|
||
}
|
||
});
|
||
|
||
// 如果模型移动到了新的区域,更新映射
|
||
if (currentZoneIndex !== closestZoneIndex) {
|
||
console.log(`[边界检测] 模型 ${modelId} 从区域 ${currentZoneIndex} 移动到区域 ${closestZoneIndex}`);
|
||
|
||
// 删除旧映射
|
||
if (currentZoneIndex !== -1) {
|
||
const oldKey = `${originalWallName}[${currentZoneIndex}]`;
|
||
appDropZone['zoneModelMap']?.delete(oldKey);
|
||
console.log(`[边界检测] 删除旧映射: ${oldKey}`);
|
||
}
|
||
|
||
// 检查目标区域是否已有模型
|
||
const newKey = `${originalWallName}[${closestZoneIndex}]`;
|
||
const existingModelId = appDropZone['zoneModelMap']?.get(newKey);
|
||
|
||
// 获取拖拽配置
|
||
const dragInfo = this.modelDragMap.get(modelId);
|
||
const handleOccupiedZone = dragInfo?.config.handleOccupiedZone ?? false;
|
||
const occupiedZoneAction = dragInfo?.config.occupiedZoneAction ?? 'return';
|
||
|
||
if (existingModelId && existingModelId !== modelId) {
|
||
console.log(`[边界检测] 目标区域 ${closestZoneIndex} 已有模型 ${existingModelId}`);
|
||
|
||
// 只有在启用占用区域处理且为 'replace' 模式下才交换位置
|
||
if (handleOccupiedZone && occupiedZoneAction === 'replace') {
|
||
console.log(`[边界检测] 配置为替换模式,交换位置`);
|
||
|
||
// 将原有模型移动到旧位置
|
||
if (currentZoneIndex !== -1) {
|
||
const swapKey = `${originalWallName}[${currentZoneIndex}]`;
|
||
appDropZone['zoneModelMap']?.set(swapKey, existingModelId);
|
||
console.log(`[边界检测] 模型 ${existingModelId} 移动到区域 ${currentZoneIndex}`);
|
||
|
||
// 实际移动被替换模型的物理位置
|
||
const existingMeshes = this.mainApp.appModel?.modelDic?.Get(existingModelId);
|
||
if (existingMeshes && existingMeshes.length) {
|
||
const existingRootMesh = existingMeshes[0];
|
||
const swapZone = wallZones[currentZoneIndex];
|
||
if (swapZone) {
|
||
const offsetDistance = 0;
|
||
const swapPosition = swapZone.center.add(swapZone.normal.scale(offsetDistance));
|
||
existingRootMesh.position.copyFrom(swapPosition);
|
||
|
||
// 更新旋转
|
||
const targetDirection = swapZone.normal.scale(-1);
|
||
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||
existingRootMesh.rotation.y = angle;
|
||
|
||
console.log(`[边界检测] 已将模型 ${existingModelId} 物理移动到区域 ${currentZoneIndex}`);
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
console.log(`[边界检测] 未启用替换模式或未启用占用区域处理,允许重叠`);
|
||
}
|
||
}
|
||
|
||
// 添加新映射
|
||
appDropZone['zoneModelMap']?.set(newKey, modelId);
|
||
console.log(`[边界检测] 添加新映射: ${newKey} -> ${modelId}`);
|
||
} else {
|
||
console.log(`[边界检测] 模型 ${modelId} 仍在区域 ${currentZoneIndex},无需更新映射`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理资源
|
||
*/
|
||
dispose(): void {
|
||
// 移除所有拖拽行为
|
||
this.modelDragMap.forEach((dragInfo, modelId) => {
|
||
if (dragInfo.behavior) {
|
||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||
if (meshes && meshes.length) {
|
||
meshes[0].removeBehavior(dragInfo.behavior);
|
||
}
|
||
}
|
||
});
|
||
this.modelDragMap.clear();
|
||
}
|
||
}
|