"""
选择和变换系统模块

负责物体选择和变换相关功能：
- 选择框的创建和更新
- 坐标轴(Gizmo)系统
- 拖拽变换逻辑
- 射线检测和碰撞检测
"""
from direct.showbase.ShowBaseGlobal import globalClock
from panda3d.core import (Vec3, Point3, Point2, LineSegs, ColorAttrib, RenderState,
                          DepthTestAttrib, CollisionTraverser, CollisionHandlerQueue,
                          CollisionNode, CollisionRay, GeomNode, BitMask32, Material, LColor, DepthWriteAttrib,
                          TransparencyAttrib, Vec4, CollisionCapsule)
from direct.task.TaskManagerGlobal import taskMgr
import math

class SelectionSystem:
    """选择和变换系统类"""

    def __init__(self, world):
        """初始化选择系统

        Args:
            world: 核心世界对象引用
        """
        self.world = world

        # 选择相关状态
        self.selectedNode = None
        self.selectionBox = None  # 选择框
        self.selectionBoxTarget = None  # 选择框跟踪的目标节点

        # 坐标轴工具（Gizmo）相关
        self.gizmo = None  # 坐标轴
        self.gizmoTarget = None  # 坐标轴跟踪的目标节点
        self.gizmoXAxis = None  # X轴
        self.gizmoYAxis = None  # Y轴
        self.gizmoZAxis = None  # Z轴
        self.gizmoRotXAxis = None
        self.gizmoRotYAxis = None
        self.gizmoRotZAxis = None
        self.axis_length = 5.0  # 坐标轴长度（增加到5.0）

        # 拖拽相关状态
        self.isDraggingGizmo = False  # 是否正在拖拽坐标轴
        self.dragGizmoAxis = None  # 当前拖拽的轴（"x", "y", "z"）
        self.gizmoStartPos = None  # 拖拽开始时坐标轴的位置
        self.gizmoTargetStartPos = None  # 拖拽开始时目标节点的位置
        self.dragStartMousePos = None  # 拖拽开始时的鼠标位置

        # 高亮相关
        self.gizmoHighlightAxis = None
        self.gizmo_colors = {
            "x": (1, 0, 0, 1.0),  # 红色
            "y": (0, 1, 0, 1.0),  # 绿色
            "z": (0, 0, 1, 1.00)   # 蓝色
        }
        self.gizmo_highlight_colors = {
            "x": (1.0, 1.0, 0.0, 1.0),  # 黄色高亮
            "y": (1.0, 1.0, 0.0, 1.0),  # 黄色高亮
            "z": (1.0, 1.0, 0.0, 1.0)   # 黄色高亮
        }

        #性能优化相关
        self._optimized_node = False
        self._last_update_time = 0
        self._cached_bounds = {}
        self._gizmo_update_interval = 0.1
        self._selection_box_update_interval = 0.2

        self._current_cursor = None
        self._default_cursor = None

        self._last_click_time = 0
        self._double_click_threshold = 0.3
        self._last_clicked_node = None
        self._double_click_task = None

        print("✓ 选择和变换系统初始化完成")
    # ==================== 光标设置 ====================
    def _setCursor(self,cursor_type):
        try:
            from PyQt5.QtCore import Qt
            if self._current_cursor == cursor_type:
                return
            if hasattr(self.world,'main_window') and self.world.main_window:
                main_window = self.world.main_window
            else:
                from PyQt5.QtWidgets import QApplication
                main_window = QApplication.activeWindow()
                if not main_window:
                    windows = QApplication.topLevelWindows()
                    for window in windows:
                        if hasattr(window,'isVisible') and window.isVisible():
                            main_window = window
                            break
            if main_window:
                if cursor_type == "crosshair":
                    main_window.setCursor(Qt.CrossCursor)
                elif cursor_type == "size_hor":
                    main_window.setCursor(Qt.SizeHorCursor)
                elif cursor_type == "size_ver":
                    main_window.setCursor(Qt.SizeVerCursor)
                elif cursor_type == "size_all":
                    main_window.setCursor(Qt.SizeAllCursor)
                elif cursor_type == "pointing_hand":
                    main_window.setCursor(Qt.PointingHandCursor)
                else:
                    main_window.unsetCursor()
                self._current_cursor = cursor_type
                #print(f"光标已设置:{cursor_type}")
                self._current_cursor = cursor_type
            else:
                print("警告：无法获取主窗口，光标设置失败")
        except Exception as e:
            print(f"设置光标失败{e}")
    def _resetCursor(self):
        self._setCursor("default")

    # ==================== 选择框系统 ====================

    def createSelectionBox(self, nodePath):
        """为选中的节点创建选择框"""
        try:
            if self.selectionBox:
                #print("  移除现有选择框")
                self.selectionBox.removeNode()
                self.selectionBox = None

            if not nodePath:
                print("  目标节点为空，取消创建")
                return

            self.selectionBox = self.world.render.attachNewNode("selectionBox")
            self.selectionBoxTarget = nodePath

            taskMgr.add(self.updateSelectionBoxTask, "updateSelectionBox")
            self.updateSelectionBoxGeometry()

        except Exception as e:
            print(f"  ✗ 创建选择框失败: {str(e)}")
            import traceback
            traceback.print_exc()

    def updateSelectionBoxGeometry(self):
        """更新选择框的几何形状和位置"""
        try:
            if not self.selectionBox or not self.selectionBoxTarget:
                return

            if self.selectionBoxTarget.isEmpty():
                return

            minPoint = Point3()
            maxPoint = Point3()

            try:
                has_bounds = self.selectionBoxTarget.calcTightBounds(minPoint, maxPoint, self.world.render)
                if not has_bounds:
                    return
            except:
                return

            # 检查边界框的有效性
            if (minPoint.x > maxPoint.x or minPoint.y > maxPoint.y or minPoint.z > maxPoint.z or
                    abs(minPoint.x) > 1e10 or abs(minPoint.y) > 1e10 or abs(minPoint.z) > 1e10 or
                    abs(maxPoint.x) > 1e10 or abs(maxPoint.y) > 1e10 or abs(maxPoint.z) > 1e10):
                print("警告: 检测到无效的边界框，跳过选择框更新")
                return

            # 检查是否需要重新计算边界框
            if not hasattr(self, '_bounds_cache'):
                self._bounds_cache = {}

            node_name = self.selectionBoxTarget.getName()
            import time
            current_time = time.time()

            # 如果缓存存在且未过期，则使用缓存
            if (node_name in self._bounds_cache and
                    current_time - self._bounds_cache[node_name]['time'] < 0.1):
                minPoint, maxPoint = self._bounds_cache[node_name]['bounds']
            else:
                # 计算新的边界框并缓存
                minPoint = Point3()
                maxPoint = Point3()
                if not self.selectionBoxTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
                    return

                # 缓存结果
                self._bounds_cache[node_name] = {
                    'bounds': (minPoint, maxPoint),
                    'time': current_time
                }

                # 清理旧缓存
                expired_keys = [k for k, v in self._bounds_cache.items()
                                if current_time - v['time'] > 1.0]
                for key in expired_keys:
                    del self._bounds_cache[key]

            # 清除现有的几何体
            self.selectionBox.removeNode()
            self.selectionBox = self.world.render.attachNewNode("selectionBox")

            # 获取目标节点在世界坐标系中的边界框（使用正确的API）
            minPoint = Point3()
            maxPoint = Point3()
            if not self.selectionBoxTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
                return

            # 获取边界框的最小和最大点（世界坐标）
            #print(f"世界边界框: min={minPoint}, max={maxPoint}")

            # 创建线段对象
            lines = LineSegs()
            lines.setThickness(2.0)

            # 定义立方体的8个顶点
            vertices = [
                (minPoint.x, minPoint.y, minPoint.z),  # 0: 前下左
                (maxPoint.x, minPoint.y, minPoint.z),  # 1: 前下右
                (maxPoint.x, maxPoint.y, minPoint.z),  # 2: 后下右
                (minPoint.x, maxPoint.y, minPoint.z),  # 3: 后下左
                (minPoint.x, minPoint.y, maxPoint.z),  # 4: 前上左
                (maxPoint.x, minPoint.y, maxPoint.z),  # 5: 前上右
                (maxPoint.x, maxPoint.y, maxPoint.z),  # 6: 后上右
                (minPoint.x, maxPoint.y, maxPoint.z),  # 7: 后上左
            ]

            # 定义立方体的边（连接顶点的线段）
            edges = [
                # 底面
                (0, 1), (1, 2), (2, 3), (3, 0),
                # 顶面
                (4, 5), (5, 6), (6, 7), (7, 4),
                # 垂直边
                (0, 4), (1, 5), (2, 6), (3, 7)
            ]

            # 绘制所有边
            for start, end in edges:
                lines.moveTo(*vertices[start])
                lines.drawTo(*vertices[end])

            # 创建选择框几何体
            geomNode = lines.create()
            self.selectionBox.attachNewNode(geomNode)

            # 设置选择框的颜色为亮橙色
            self.selectionBox.setColor(1.0, 0.5, 0.0, 1.0)

            # 设置渲染状态，确保线框总是在最前面显示
            state = RenderState.make(
                DepthTestAttrib.make(DepthTestAttrib.MLess),
                ColorAttrib.makeFlat((1.0, 0.5, 0.0, 1.0))
            )
            self.selectionBox.setState(state)

            # 确保选择框不被光照影响
            self.selectionBox.setLightOff()

            # 让选择框稍微大一点，避免与模型重叠
            self.selectionBox.setScale(1.01)

        except Exception as e:
            print(f"更新选择框几何体失败: {str(e)}")
            import traceback
            traceback.print_exc()

    def updateSelectionBoxTask(self, task):
        """选择框更新任务 - 平衡性能和实时性"""
        try:
            update_interval = 0.05

            if not hasattr(self, '_last_selection_box_update'):
                self._last_selection_box_update = 0

            import time
            current_time = time.time()
            if current_time - self._last_selection_box_update < update_interval:
                return task.cont
            self._last_selection_box_update = current_time

            # 检查目标节点是否已被删除
            self.checkAndClearIfTargetDeleted()

            if not self.selectionBox or not self.selectionBoxTarget:
                return task.done

            # 检查目标节点是否还存在
            if self.selectionBoxTarget.isEmpty():
                self.clearSelectionBox()
                return task.done

            # 检查目标节点是否发生了变化（位置、旋转、缩放）
            current_transform = self._getNodeTransformKey(self.selectionBoxTarget)

            if (not hasattr(self, '_last_transform_key') or
                    self._last_transform_key != current_transform):
                # 节点发生了变化，更新选择框
                self.updateSelectionBoxGeometry()
                self._last_transform_key = current_transform

            return task.cont

        except Exception as e:
            print(f"选择框更新任务出错: {str(e)}")
            return task.done

    def _getNodeTransformKey(self, node):
        """获取节点变换的关键信息，用于快速比较"""
        try:
            # 获取节点的关键变换信息
            pos = node.getPos(self.world.render)
            hpr = node.getHpr(self.world.render)
            scale = node.getScale(self.world.render)

            # 返回一个可以比较的元组
            return (pos.x, pos.y, pos.z, hpr.x, hpr.y, hpr.z, scale.x, scale.y, scale.z)
        except:
            return None

    def clearSelectionBox(self):
        """清除选择框"""
        if self.selectionBox:
            self.selectionBox.removeNode()
            self.selectionBox = None

        # 停止选择框更新任务
        taskMgr.remove("updateSelectionBox")

        # 清除目标节点引用
        self.selectionBoxTarget = None

        print("清除了选择框")

    # ==================== 坐标轴(Gizmo)系统 ====================

    def createGizmo(self, nodePath):
        """为选中的节点创建坐标轴工具 - 保留箭头版本"""
        try:
            #print(f"  开始创建坐标轴，目标节点: {nodePath.getName()}")

            # 如果已有坐标轴，先移除
            if self.gizmo:
                self.gizmo.removeNode()
                self.gizmo = None
                taskMgr.remove("updateGizmo")

            if not nodePath:
                return

                # 创建坐标轴主节点
            self.gizmo = self.world.render.attachNewNode("gizmo")
            self.gizmoTarget = nodePath

            # 添加标识标签，便于识别
            self.gizmo.setTag("is_gizmo", "1")
            if hasattr(nodePath, 'getName'):
                self.gizmo.setTag("gizmo_target", nodePath.getName())

            # 为各轴添加标签
            if hasattr(self, 'gizmoXAxis') and self.gizmoXAxis:
                self.gizmoXAxis.setTag("is_gizmo", "1")
                self.gizmoXAxis.setTag("gizmo_axis", "x")
            if hasattr(self, 'gizmoYAxis') and self.gizmoYAxis:
                self.gizmoYAxis.setTag("is_gizmo", "1")
                self.gizmoYAxis.setTag("gizmo_axis", "y")
            if hasattr(self, 'gizmoZAxis') and self.gizmoZAxis:
                self.gizmoZAxis.setTag("is_gizmo", "1")
                self.gizmoZAxis.setTag("gizmo_axis", "z")

            # 设置位置和朝向
            minPoint = Point3()
            maxPoint = Point3()
            if nodePath.calcTightBounds(minPoint, maxPoint, self.world.render):
                center = Point3((minPoint.x + maxPoint.x) * 0.5,
                                (minPoint.y + maxPoint.y) * 0.5,
                                (minPoint.z + maxPoint.z) * 0.5)
                self.gizmo.setPos(center)

            parent_node = nodePath.getParent()
            if parent_node and parent_node != self.world.render:
                self.gizmo.setHpr(parent_node.getHpr())
            else:
                self.gizmo.setHpr(0, 0, 0)

                # 只调用一次几何体创建
            self.createGizmoGeometry()

            #只调用一次颜色设置
            self.setGizmoAxisColor("x", self.gizmo_colors["x"])
            self.setGizmoAxisColor("y", self.gizmo_colors["y"])
            self.setGizmoAxisColor("z", self.gizmo_colors["z"])

            self._updateGizmoScreenSize()

            self._setupGizmoRendering()

            self.setupGizmoCollision()

            # 现在才显示坐标轴
            self.gizmo.show()

            # 只启动一次更新任务
            taskMgr.add(self.updateGizmoTask, "updateGizmo")

            #print(f"  ✓ 为节点 {nodePath.getName()} 创建了坐标轴")

        except Exception as e:
            print(f"创建坐标轴失败: {str(e)}")
    def createGizmoGeometry(self):
        """创建坐标轴的几何体"""
        from panda3d.core import Material
        try:
            if not self.gizmo:
                return

            is_scale_tool = self.world.tool_manager.isScaleTool() if self.world.tool_manager else False
            is_rotate_tool = self.world.tool_manager.isRotateTool() if self.world.tool_manager else False

            import os
            from panda3d.core import getModelPath, Filename
            
            # 获取项目根目录
            base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
            
            # 确保core目录在模型搜索路径中
            model_path = getModelPath()
            core_dir = os.path.join(base_dir, "core")
            core_filename = Filename.from_os_specific(core_dir)
            if not model_path.findFile(core_filename):
                model_path.appendDirectory(core_filename)
                print(f"✓ 添加core目录到模型搜索路径: {core_dir}")

            if is_scale_tool:
                model_filename = "core/UniformScaleHandle.fbx"
            elif is_rotate_tool:
                model_filename = "core/RotationHandleQuarter.fbx"
                arrow_filename = "core/TranslateArrowHandle.fbx"
            else:
                model_filename = "core/TranslateArrowHandle.fbx"

            # model_paths = [
            #     "core/TranslateArrowHandle.fbx",
            #     "EG/core/TranslateArrowHandle.fbx",
            # ]
            gizmo_model = None
            gizmoRot_model = None
            try:
                if is_rotate_tool:
                    gizmo_model = self.world.loader.loadModel(arrow_filename)
                    gizmoRot_model = self.world.loader.loadModel(model_filename)
                else:
                    gizmo_model = self.world.loader.loadModel(model_filename)
            except Exception as e:
                print(f"加载模型失败: {e}")
                return

            x_rHandle = None
            y_rHandle = None
            z_rHandle = None

            if is_rotate_tool:
                self.gizmoRotXAxis = self.gizmo.attachNewNode("gizmo_rot_x_axis")
                x_rHandle = gizmoRot_model.copyTo(self.gizmoRotXAxis)
                x_rHandle.setName("x_handle")
                self.gizmoRotYAxis = self.gizmo.attachNewNode("gizmo_rot_y_axis")
                y_rHandle = gizmoRot_model.copyTo(self.gizmoRotYAxis)
                y_rHandle.setName("y_handle")
                self.gizmoRotZAxis = self.gizmo.attachNewNode("gizmo_rot_z_axis")
                z_rHandle = gizmoRot_model.copyTo(self.gizmoRotZAxis)
                z_rHandle.setName("z_handle")

            self.gizmoXAxis = self.gizmo.attachNewNode("gizmo_x_axis")
            x_handle = gizmo_model.copyTo(self.gizmoXAxis)
            x_handle.setName("x_handle")

            self.gizmoYAxis = self.gizmo.attachNewNode("gizmo_y_axis")
            y_handle = gizmo_model.copyTo(self.gizmoYAxis)
            y_handle.setName("y_handle")

            self.gizmoZAxis = self.gizmo.attachNewNode("gizmo_z_axis")
            z_handle = gizmo_model.copyTo(self.gizmoZAxis)
            z_handle.setName("z_handle")

            if is_scale_tool:
                x_handle.setHpr(0,-90,0)
                x_handle.setScale(0.6,0.03,0.03)
                x_handle.setPos(2.2,0,0)

                y_handle.setHpr(90,0,0)
                y_handle.setScale(0.6,0.03,0.03)
                y_handle.setPos(0,2.2,0)

                z_handle.setHpr(0,0,-90)
                z_handle.setScale(0.6,0.03,0.03)
                z_handle.setPos(0,0,2.2)
            elif is_rotate_tool:
                x_rHandle.setHpr(0,0,90)
                x_rHandle.setScale(0.025,0.0125,0.0125)
                x_rHandle.setPos(0,0,0)

                y_rHandle.setHpr(0,0,0)
                y_rHandle.setScale(0.025,0.0125,0.0125)
                y_rHandle.setPos(0,0,0)

                z_rHandle.setHpr(-90,0,0)
                z_rHandle.setScale(0.025,0.0125,0.0125)
                z_rHandle.setPos(0,0,0)

                x_handle.setHpr(0, -90, 0)
                x_handle.setScale(0.1, 0.05, 0.05)
                x_handle.setPos(0, 0, 0)

                y_handle.setHpr(90, 0, 0)
                y_handle.setScale(0.1, 0.05, 0.05)
                y_handle.setPos(0, 0, 0)

                z_handle.setHpr(0, 0, -90)
                z_handle.setScale(0.1, 0.05, 0.05)
                z_handle.setPos(0, 0, 0)

                self.setGizmoRotAxisColor("x", self.gizmo_colors["x"])
                self.setGizmoRotAxisColor("y", self.gizmo_colors["y"])
                self.setGizmoRotAxisColor("z", self.gizmo_colors["z"])
            else:
                x_handle.setHpr(0,-90,0)
                x_handle.setScale(0.1,0.05,0.05)
                x_handle.setPos(0,0,0)

                y_handle.setHpr(90,0,0)
                y_handle.setScale(0.1,0.05,0.05)
                y_handle.setPos(0,0,0)

                z_handle.setHpr(0,0,-90)
                z_handle.setScale(0.1,0.05,0.05)
                z_handle.setPos(0,0,0)

            # 设置初始颜色
            self.setGizmoAxisColor("x", self.gizmo_colors["x"])
            self.setGizmoAxisColor("y", self.gizmo_colors["y"])
            self.setGizmoAxisColor("z", self.gizmo_colors["z"])

            #设置渲染属性，解决模型遮挡和阴影问题
            self._setupGizmoRendering()

        except Exception as e:
            print(f"创建坐标轴几何体失败: {str(e)}")

    def _setupGizmoRendering(self):
        try:
            axis_nodes = [self.gizmoXAxis,self.gizmoYAxis,self.gizmoZAxis]
            axis_Rotnodes = [self.gizmoRotXAxis, self.gizmoRotYAxis, self.gizmoRotZAxis]

            # 设置坐标轴主节点的渲染属性
            if self.gizmo:
                self.gizmo.setLightOff()  # 禁用光照
                self.gizmo.setShaderOff()  # 禁用着色器
                self.gizmo.setFogOff()  # 禁用雾效
                self.gizmo.setBin("fixed", 40)  # 设置为fixed渲染层级，数值越大越优先
                self.gizmo.setDepthWrite(False)  # 禁用深度写入
                self.gizmo.setDepthTest(False)  # 禁用深度测试，确保始终可见

            # 设置各轴节点的渲染属性
            for axis_node in axis_nodes:
                if axis_node:
                    axis_node.setLightOff()
                    axis_node.setShaderOff()
                    axis_node.setFogOff()
                    axis_node.setBin("fixed", 40)  # 与主节点相同优先级
                    axis_node.setDepthWrite(False)  # 禁用深度写入
                    axis_node.setDepthTest(False)  # 禁用深度测试

            # 设置旋转轴节点的渲染属性
            for axis_rotnode in axis_Rotnodes:
                if axis_rotnode:
                    axis_rotnode.setLightOff()
                    axis_rotnode.setShaderOff()
                    axis_rotnode.setFogOff()
                    axis_rotnode.setBin("fixed", 40)  # 与主节点相同优先级
                    axis_rotnode.setDepthWrite(False)  # 禁用深度写入
                    axis_rotnode.setDepthTest(False)  # 禁用深度测试

                # 收集所有handle节点
            arrow_nodes = []
            if self.gizmoXAxis:
                x_handle = self.gizmoXAxis.find("x_handle")
                if x_handle and not x_handle.isEmpty():
                    arrow_nodes.append(x_handle)
            if self.gizmoYAxis:
                y_handle = self.gizmoYAxis.find("y_handle")
                if y_handle and not y_handle.isEmpty():
                    arrow_nodes.append(y_handle)
            if self.gizmoZAxis:
                z_handle = self.gizmoZAxis.find("z_handle")
                if z_handle and not z_handle.isEmpty():
                    arrow_nodes.append(z_handle)

            rot_nodes = []
            if self.gizmoRotXAxis:
                x_rHandle = self.gizmoRotXAxis.find("x_handle")
                if x_rHandle and not x_rHandle.isEmpty():
                    rot_nodes.append(x_rHandle)
            if self.gizmoRotYAxis:
                y_rHandle = self.gizmoRotYAxis.find("y_handle")
                if y_rHandle and not y_rHandle.isEmpty():
                    rot_nodes.append(y_rHandle)
            if self.gizmoRotZAxis:
                z_rHandle = self.gizmoRotZAxis.find("z_handle")
                if z_rHandle and not z_rHandle.isEmpty():
                    rot_nodes.append(z_rHandle)

            # 设置handle节点的渲染属性
            for arrow_node in arrow_nodes:
                if arrow_node:
                    arrow_node.setLightOff()
                    arrow_node.setShaderOff()
                    arrow_node.setFogOff()
                    arrow_node.setBin("fixed", 41)  # 略高于主节点，确保最优先显示
                    arrow_node.setDepthWrite(False)
                    arrow_node.setDepthTest(False)
                    # 启用透明度支持
                    arrow_node.setTransparency(TransparencyAttrib.MAlpha)

            for rot_node in rot_nodes:
                if rot_node:
                    rot_node.setLightOff()
                    rot_node.setShaderOff()
                    rot_node.setFogOff()
                    rot_node.setBin("fixed", 41)  # 略高于主节点，确保最优先显示
                    rot_node.setDepthWrite(False)
                    rot_node.setDepthTest(False)
                    # 启用透明度支持
                    rot_node.setTransparency(TransparencyAttrib.MAlpha)

        except Exception as e:
            print(f"设置坐标轴渲染属性失败: {str(e)}")

    def updateGizmoTask(self, task):
        """坐标轴更新任务 - 包含固定大小功能"""
        try:
            # 限制更新频率
            if not hasattr(self, '_last_gizmo_update'):
                self._last_gizmo_update = 0

            import time
            current_time = time.time()
            if current_time - self._last_gizmo_update < 0.5:  # 每0.05秒更新一次
                return task.cont
            self._last_gizmo_update = current_time

            #检查目标节点是否已被删除
            self.checkAndClearIfTargetDeleted()

            if not self.gizmo or not self.gizmoTarget:
                return task.done

            # 检查目标节点是否还存在
            if self.gizmoTarget.isEmpty():
                self.clearGizmo()
                return task.done

            is_scale_tool = self.world.tool_manager.isScaleTool() if self.world.tool_manager else False
            is_rotate_tool = self.world.tool_manager.isRotateTool() if self.world.tool_manager else False
            was_scale_tool = getattr(self,'_last_tool_scale_state',False)
            was_rotate_tool =getattr(self,'_last_tool_rotate_state',False)

            tool_changed = (is_scale_tool!=was_scale_tool) or (is_rotate_tool != was_rotate_tool)

            if tool_changed:
                self._last_tool_scale_state = is_scale_tool
                self._last_tool_rotate_state = is_rotate_tool

                if self.gizmoXAxis:
                    self.gizmoXAxis.removeNode()
                    self.gizmoXAxis = None
                if self.gizmoYAxis:
                    self.gizmoYAxis.removeNode()
                    self.gizmoYAxis = None
                if self.gizmoZAxis:
                    self.gizmoZAxis.removeNode()
                    self.gizmoZAxis = None
                if self.gizmoRotXAxis:
                    self.gizmoRotXAxis.removeNode()
                    self.gizmoRotXAxis = None
                if self.gizmoRotYAxis:
                    self.gizmoRotYAxis.removeNode()
                    self.gizmoRotYAxis = None
                if self.gizmoRotZAxis:
                    self.gizmoRotZAxis.removeNode()
                    self.gizmoRotZAxis = None

                self.createGizmoGeometry()

                self.setGizmoAxisColor("x",self.gizmo_colors["x"])
                self.setGizmoAxisColor("y",self.gizmo_colors["y"])
                self.setGizmoAxisColor("z",self.gizmo_colors["z"])

                self.setupGizmoCollision()

            light_object = self.gizmoTarget.getPythonTag("rp_light_object")
            if light_object:
                light_pos = light_object.pos
                self.gizmo.setPos(light_object.pos)
                self.gizmoTarget.setPos(light_pos)
            else:
                # 只在必要时更新位置和朝向
                self._updateGizmoPositionAndOrientation()

            # 【新功能】：动态调整坐标轴大小，保持固定的屏幕大小
            self._updateGizmoScreenSize()

            return task.cont

        except Exception as e:
            print(f"坐标轴更新任务出错: {str(e)}")
            return task.done

    def _updateGizmoPositionAndOrientation(self):
        """优化的Gizmo位置和朝向更新"""
        # 只在必要时重新计算边界框
        if not hasattr(self, '_last_gizmo_bounds_update'):
            self._last_gizmo_bounds_update = 0

        import time
        current_time = time.time()
        if current_time - self._last_gizmo_bounds_update > 0.2:  # 每0.2秒计算一次边界框
            minPoint = Point3()
            maxPoint = Point3()
            # 添加异常处理
            try:
                if self.gizmoTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
                    # 检查边界框的有效性
                    if (abs(minPoint.x) < 1e10 and abs(minPoint.y) < 1e10 and abs(minPoint.z) < 1e10 and
                            abs(maxPoint.x) < 1e10 and abs(maxPoint.y) < 1e10 and abs(maxPoint.z) < 1e10):
                        # 计算中心点
                        center = Point3((minPoint.x + maxPoint.x) * 0.5,
                                        (minPoint.y + maxPoint.y) * 0.5,
                                        (minPoint.z + maxPoint.z) * 0.5)
                        self.gizmo.setPos(center)
            except Exception as e:
                print(f"更新Gizmo位置时出错: {e}")
            # if self.gizmoTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
            #     # 计算中心点
            #     center = Point3((minPoint.x + maxPoint.x) * 0.5,
            #                     (minPoint.y + maxPoint.y) * 0.5,
            #                     (minPoint.z + maxPoint.z) * 0.5)
            #     self.gizmo.setPos(center)
            self._last_gizmo_bounds_update = current_time

        is_scale_tool = self.world.tool_manager.isScaleTool() if self.world.tool_manager else False

        # 安区地更新朝向

        if is_scale_tool:
            #self.gizmo.setHpr(self.gizmoTarget.getHpr())
            self.gizmo.setQuat(self.gizmoTarget.getQuat(self.world.render))
        else:
            parent_node = self.gizmoTarget.getParent()
            if parent_node and parent_node != self.world.render:
                parent_hpr = parent_node.getHpr()
                self.gizmo.setHpr(parent_hpr)
            else:
                self.gizmo.setHpr(0,0,0)

        # 更新朝向
        # parent_node = self.gizmoTarget.getParent()
        # if parent_node and parent_node != self.world.render:
        #     parent_hpr = parent_node.getHpr()
        #     self.gizmo.setHpr(parent_hpr)
        # else:
        #     self.gizmo.setHpr(0, 0, 0)

    def _updateGizmoScreenSize(self):
        """动态调整坐标轴大小，保持固定的屏幕大小"""
        try:
            if not self.gizmo or not self.gizmoTarget:
                return
            
            # 计算相机到坐标轴的距离
            gizmo_world_pos = self.gizmo.getPos(self.world.render)
            cam_pos = self.world.cam.getPos()
            distance_to_gizmo = (cam_pos - gizmo_world_pos).length()
            
            # 获取相机视野角度和窗口尺寸
            fov = self.world.cam.node().getLens().getFov()[0]  # 水平视野角度
            fov_radians = math.radians(fov)
            winWidth, winHeight = self.world.getWindowSize()
            
            # 计算一个像素在坐标轴距离处对应的世界坐标大小
            pixel_to_world_ratio = distance_to_gizmo * math.tan(fov_radians / 2) / (winWidth / 2)
            
            # 设定坐标轴在屏幕上的期望像素长度（固定值）
            desired_screen_length = 120  # 像素
            
            # 计算世界坐标系中的轴长度
            world_axis_length = desired_screen_length * pixel_to_world_ratio
            
            # 计算缩放比例（相对于基础轴长度）
            scale_factor = world_axis_length / self.axis_length
            
            # 应用缩放到坐标轴
            self.gizmo.setScale(scale_factor)
            
            # 限制缩放范围，避免过大或过小
            min_scale = 0.001
            max_scale = 100.0
            final_scale = max(min_scale, min(max_scale, scale_factor))
            
            if final_scale != scale_factor:
                self.gizmo.setScale(final_scale)
            
        except Exception as e:
            # 静默处理错误，避免频繁输出
            pass

    def clearGizmo(self):
        """清除坐标轴"""
        if self.gizmo:
            self.gizmo.removeNode()
            self.gizmo = None

        # 停止坐标轴更新任务
        taskMgr.remove("updateGizmo")

        # 清除坐标轴相关引用
        self.gizmoTarget = None
        self.gizmoXAxis = None
        self.gizmoYAxis = None
        self.gizmoZAxis = None
        self.isDraggingGizmo = False
        self.dragGizmoAxis = None
        self.dragStartMousePos = None
        self.gizmoTargetStartPos = None
        self.gizmoStartPos = None
        self._resetCursor()


    # def setGizmoAxisColor(self, axis, color):
    #     """设置坐标轴颜色 - RenderPipeline 兼容版本"""
    #     try:
    #         from panda3d.core import AntialiasAttrib,TransparencyAttrib
    #
    #         axis_nodes = {
    #             "x": self.gizmoXAxis,
    #             "y": self.gizmoYAxis,
    #             "z": self.gizmoZAxis
    #         }
    #
    #         if axis in axis_nodes and axis_nodes[axis]:
    #             axis_node = axis_nodes[axis]
    #
    #             axis_node.setColor(color[0]*20.0,color[1]*20.0,color[2]*20.0,color[3])
    #             axis_node.setColorScale(color[0]*10.0,color[1]*10.0,color[2]*10.0,color[3])
    #             axis_node.setShaderOff(10000)
    #             axis_node.setLightOff()
    #             axis_node.setMaterialOff()
    #             axis_node.setTextureOff()
    #             axis_node.setFogOff()
    #
    #     except Exception as e:
    #         print(f"设置坐标轴颜色失败: {str(e)}")
    #         # 回退到简单的颜色设置
    #         try:
    #             if axis in axis_nodes and axis_nodes[axis]:
    #                 axis_nodes[axis].setColor(*color)
    #         except:
    #             pass


    def setGizmoRotAxisColor(self, axis, color):
        """使用材质设置坐标轴颜色 - RenderPipeline兼容版本"""
        try:
            from panda3d.core import Material, Vec4,ColorWriteAttrib,DepthWriteAttrib,DepthTestAttrib,TransparencyAttrib

            # 获取对应的轴节点
            axis_nodes = {
                "x": self.gizmoRotXAxis,
                "y": self.gizmoRotYAxis,
                "z": self.gizmoRotZAxis
            }

            if axis not in axis_nodes or not axis_nodes[axis]:
                return

            axis_node = axis_nodes[axis]

            handle_node = None
            handle_node = axis_node.find("x_handle") if axis == "x" else handle_node
            handle_node = axis_node.find("y_handle") if axis == "y" else handle_node
            handle_node = axis_node.find("z_handle") if axis == "z" else handle_node

            #如果找不到特定名称的节点，尝试查找任何子节点
            if not handle_node:
                children = axis_node.getChildren()
                if children.getNumPath()>0:
                    handle_node = children[0]

            if not handle_node:
                #print(f"未找到{axis}轴的处理模型")
                return

            # 创建或获取材质
            mat = Material()

            # 设置材质属性 - 使用更自然的颜色，避免过亮的自发光
            adjusted_color = Vec4(
                min(color[0]*20, 1.0),
                min(color[1]*20, 1.0),
                min(color[2]*20, 1.0),
                color[3]
            )

            mat.setBaseColor(adjusted_color)
            #mat.setEmission(Vec4(1, 1, 1, 1.0))  # 自发光

            # 应用材质
            handle_node.setMaterial(mat, 1)


            # 设置透明度
            if color[3] < 1.0:
                handle_node.setTransparency(TransparencyAttrib.MAlpha)
            else:
                handle_node.setTransparency(TransparencyAttrib.MNone)

            handle_node.setLightOff()  # 禁用光照影响
            handle_node.setShaderOff()  # 禁用着色器
            handle_node.setFogOff()  # 禁用雾效果

            handle_node.setBin("fixed",41)
            handle_node.setDepthWrite(False)
            handle_node.setDepthTest(False)

            # 保存材质引用以便后续修改
            if axis == "x":
                self.xMat = mat
            elif axis == "y":
                self.yMat = mat
            elif axis == "z":
                self.zMat = mat

            axis_node.setLightOff()
            axis_node.setShaderOff()
            axis_node.setFogOff()
            axis_node.setBin("fixed", 40)
            axis_node.setDepthWrite(False)
            axis_node.setDepthTest(False)

        except Exception as e:
            print(f"设置坐标轴颜色失败: {str(e)}")
            # 回退到简单颜色设置
            try:
                axis_nodes = {
                    "x": self.gizmoXAxis,
                    "y": self.gizmoYAxis,
                    "z": self.gizmoZAxis
                }

                if axis in axis_nodes and axis_nodes[axis]:
                    axis_nodes[axis].setColor(color[0], color[1], color[2], color[3])
            except:
                pass

    def setGizmoAxisColor(self, axis, color):
        """使用材质设置坐标轴颜色 - RenderPipeline兼容版本"""
        try:
            from panda3d.core import Material, Vec4,ColorWriteAttrib,DepthWriteAttrib,DepthTestAttrib,TransparencyAttrib

            # 获取对应的轴节点
            axis_nodes = {
                "x": self.gizmoXAxis,
                "y": self.gizmoYAxis,
                "z": self.gizmoZAxis
            }

            if axis not in axis_nodes or not axis_nodes[axis]:
                return

            axis_node = axis_nodes[axis]

            if axis_node.isEmpty():
                return

            handle_node = None
            handle_node = axis_node.find("x_handle") if axis == "x" else handle_node
            handle_node = axis_node.find("y_handle") if axis == "y" else handle_node
            handle_node = axis_node.find("z_handle") if axis == "z" else handle_node

            # 如果找不到特定名称的节点，尝试查找任何子节点
            if not handle_node or handle_node.isEmpty():
                children = axis_node.getChildren()
                if children.getNumPaths() > 0:
                    handle_node = children[0]

            if not handle_node:
               # print(f"未找到{axis}轴的处理模型")
                return

            # 创建或获取材质
            mat = Material()

            adjusted_color = Vec4(
                min(color[0], 1.0),
                min(color[1], 1.0),
                min(color[2], 1.0),
                color[3]
            )

            mat.setBaseColor(adjusted_color)
            #mat.setEmission(Vec4(1, 1, 1, 1.0))  # 自发光

            # 应用材质
            handle_node.setMaterial(mat, 1)

            # 设置透明度
            if color[3] < 1.0:
                handle_node.setTransparency(TransparencyAttrib.MAlpha)
            else:
                handle_node.setTransparency(TransparencyAttrib.MNone)

            handle_node.setLightOff()  # 禁用光照影响
            handle_node.setShaderOff()  # 禁用着色器
            handle_node.setFogOff()  # 禁用雾效果

            handle_node.setBin("fixed",41)
            handle_node.setDepthWrite(False)
            handle_node.setDepthTest(False)

            # 保存材质引用以便后续修改
            if axis == "x":
                self.xMat = mat
            elif axis == "y":
                self.yMat = mat
            elif axis == "z":
                self.zMat = mat

            axis_node.setLightOff()
            axis_node.setShaderOff()
            axis_node.setFogOff()
            axis_node.setBin("fixed", 40)
            axis_node.setDepthWrite(False)
            axis_node.setDepthTest(False)

        except Exception as e:
            print(f"设置坐标轴颜色失败: {str(e)}")
            # 回退到简单颜色设置
            try:
                axis_nodes = {
                    "x": self.gizmoXAxis,
                    "y": self.gizmoYAxis,
                    "z": self.gizmoZAxis
                }

                if axis in axis_nodes and axis_nodes[axis]:
                    axis_nodes[axis].setColor(color[0], color[1], color[2], color[3])
            except:
                pass

    # ==================== 鼠标交互处理 ====================

    def _onGizmoMouseEnter(self, axis):
        """鼠标进入坐标轴时的处理 - RenderPipeline 兼容版本"""
        try:
            # 黄色高亮，增加透明度以确保在 RenderPipeline 下可见
            highlight_color = (1.0, 1.0, 0.0, 1)
            self.setGizmoAxisColor(axis, highlight_color)

            # 额外的视觉反馈
            axis_nodes = {
                "x": self.gizmoXAxis,
                "y": self.gizmoYAxis,
                "z": self.gizmoZAxis
            }

            if axis in axis_nodes and axis_nodes[axis]:
                # 稍微放大以增强视觉效果
                axis_nodes[axis].setScale(1.1)

            self.gizmoHighlightAxis = axis

        except Exception as e:
            print(f"鼠标进入坐标轴处理失败: {e}")

    def _onGizmoMouseLeave(self, axis):
        """鼠标离开坐标轴时的处理 - RenderPipeline 兼容版本"""
        try:
            # 恢复原始颜色
            original_colors = {
                "x": (1.0, 0.0, 0.0, 1.0),  # 红色
                "y": (0.0, 1.0, 0.0, 1.0),  # 绿色
                "z": (0.0, 0.0, 1.0, 1.0)   # 蓝色
            }

            if axis in original_colors:
                self.setGizmoAxisColor(axis, original_colors[axis])

            # 恢复原始大小
            axis_nodes = {
                "x": self.gizmoXAxis,
                "y": self.gizmoYAxis,
                "z": self.gizmoZAxis
            }

            if axis in axis_nodes and axis_nodes[axis]:
                axis_nodes[axis].setScale(1.0)

            if self.gizmoHighlightAxis == axis:
                self.gizmoHighlightAxis = None

        except Exception as e:
            print(f"鼠标离开坐标轴处理失败: {e}")

    def setupGizmoMouseEvents(self):
        """设置坐标轴的鼠标事件"""
        try:
            from direct.showbase.DirectObject import DirectObject

            # 为每个轴设置鼠标事件
            axis_nodes = {
                "x": self.gizmoXAxis,
                "y": self.gizmoYAxis,
                "z": self.gizmoZAxis
            }

            for axis_name, axis_node in axis_nodes.items():
                if axis_node:
                    # 设置碰撞检测标签
                    axis_node.setTag("gizmo_axis", axis_name)
                    axis_node.setTag("pickable", "1")


        except Exception as e:
            print(f"设置坐标轴鼠标事件失败: {e}")

    # ==================== 射线检测和碰撞检测 ====================

    def checkGizmoClick(self, mouseX, mouseY):
        """使用屏幕空间检测是否点击了坐标轴"""
        if not self.gizmo or not self.gizmoTarget:
            return None

        # 基本参数验证
        if not isinstance(mouseX, (int, float)) or not isinstance(mouseY, (int, float)):
            return None

        try:
            # 获取坐标轴中心的世界坐标
            gizmo_world_pos = self.gizmo.getPos(self.world.render)

            # 计算各轴端点的世界坐标
            x_end = gizmo_world_pos + Vec3(self.axis_length, 0, 0)
            y_end = gizmo_world_pos + Vec3(0, self.axis_length, 0)
            z_end = gizmo_world_pos + Vec3(0, 0, self.axis_length)

            # 使用Panda3D的内置投影方法
            def worldToScreen(world_pos):
                """将世界坐标转换为屏幕坐标"""
                # 将世界坐标转换为相机空间
                cam_space_pos = self.world.cam.getRelativePoint(self.world.render, world_pos)

                # 检查是否在相机前方
                if cam_space_pos.getY() <= 0:
                    return None

                # 使用相机的镜头进行投影
                screen_pos = Point2()
                if self.world.cam.node().getLens().project(cam_space_pos, screen_pos):
                    # 获取准确的窗口尺寸
                    win_width, win_height = self.world.getWindowSize()

                    # 转换为窗口像素坐标
                    win_x = (screen_pos.getX() + 1.0) * 0.5 * win_width
                    win_y = (1.0 - screen_pos.getY()) * 0.5 * win_height
                    return (win_x, win_y)
                return None

            # 投影各个关键点
            center_screen = worldToScreen(gizmo_world_pos)
            x_screen = worldToScreen(x_end)
            y_screen = worldToScreen(y_end)
            z_screen = worldToScreen(z_end)

            # 如果无法获得屏幕坐标，使用备用方法
            if not center_screen:
                return self.checkGizmoClickFallback(mouseX, mouseY)

            # 计算点击阈值
            click_threshold = 15  # 增大检测范围

            # 检测各个轴，对于端点在屏幕外的轴提供回退方案
            def getClickDetectionPoint(axis_name, original_screen_pos):
                if original_screen_pos:
                    return original_screen_pos
                # 如果端点在屏幕外，使用轴长度的一半作为检测点
                if axis_name == "x":
                    half_end = gizmo_world_pos + Vec3(self.axis_length * 0.5, 0, 0)
                elif axis_name == "y":
                    half_end = gizmo_world_pos + Vec3(0, self.axis_length * 0.5, 0)
                elif axis_name == "z":
                    half_end = gizmo_world_pos + Vec3(0, 0, self.axis_length * 0.5)
                else:
                    return None
                return worldToScreen(half_end)

            axes_data = [
                ("x", getClickDetectionPoint("x", x_screen), "X轴"),
                ("y", getClickDetectionPoint("y", y_screen), "Y轴"),
                ("z", getClickDetectionPoint("z", z_screen), "Z轴")
            ]

            for axis_name, axis_screen, axis_label in axes_data:
                if axis_screen:
                    # 计算鼠标到轴线的距离
                    distance = self.distanceToLine(
                        (mouseX, mouseY), center_screen, axis_screen
                    )
                    #print(f"{axis_label}距离: {distance:.2f}")

                    if distance < click_threshold:
                        #print(f"✓ 点击了{axis_label}")
                        return axis_name

            return None

        except Exception as e:
            print(f"坐标轴点击检测失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def checkGizmoClickFallback(self, mouseX, mouseY):
        """备用检测方法：使用固定的屏幕区域"""

        # 获取准确的窗口尺寸
        win_width, win_height = self.world.getWindowSize()

        # 获取窗口中心作为参考点
        center_x = win_width // 2
        center_y = win_height // 2

        # 定义相对于中心的轴区域（简化假设坐标轴在屏幕中心附近）
        axis_length_pixels = 100  # 假设轴长度在屏幕上约100像素

        # X轴：从中心向右
        x_start = (center_x, center_y)
        x_end = (center_x + axis_length_pixels, center_y)

        # Y轴：从中心向上（注意Y轴方向）
        y_start = (center_x, center_y)
        y_end = (center_x, center_y - axis_length_pixels)

        # Z轴：从中心向右上45度
        z_start = (center_x, center_y)
        z_end = (center_x + axis_length_pixels * 0.7, center_y - axis_length_pixels * 0.7)

        threshold = 25

        # 检测各轴
        if self.distanceToLine((mouseX, mouseY), x_start, x_end) < threshold:
            print("✓ 备用方法检测到X轴")
            return "x"
        elif self.distanceToLine((mouseX, mouseY), y_start, y_end) < threshold:
            print("✓ 备用方法检测到Y轴")
            return "y"
        elif self.distanceToLine((mouseX, mouseY), z_start, z_end) < threshold:
            print("✓ 备用方法检测到Z轴")
            return "z"

        print("× 备用方法也没有检测到")
        return None

    def distanceToLine(self, point, line_start, line_end):
        """计算点到线段的距离"""
        try:
            px, py = point
            x1, y1 = line_start
            x2, y2 = line_end

            # 计算线段长度
            line_length = ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5
            if line_length == 0:
                return ((px - x1) ** 2 + (py - y1) ** 2) ** 0.5

            # 计算点到线的距离
            t = max(0, min(1, ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / (line_length ** 2)))
            projection_x = x1 + t * (x2 - x1)
            projection_y = y1 + t * (y2 - y1)

            distance = ((px - projection_x) ** 2 + (py - projection_y) ** 2) ** 0.5
            return distance

        except Exception as e:
            print(f"距离计算错误: {e}")
            return float('inf')

    # ==================== 高亮和交互 ====================

    def detectGizmoAxisAtMouse(self, mouseX, mouseY):
        """统一的坐标轴检测方法 - 同时用于高亮和点击检测"""
        if not self.gizmo or not self.gizmoTarget:
            return None

        try:
            # 获取坐标轴中心的世界坐标
            gizmo_world_pos = self.gizmo.getPos(self.world.render)

            #获取坐标轴的世界朝向（考虑旋转）
            gizmo_world_quat = self.gizmo.getQuat(self.world.render)

            #计算各轴在世界坐标系中的实际方向向量
            x_axis_world = gizmo_world_quat.xform(Vec3(1,0,0))
            y_axis_world = gizmo_world_quat.xform(Vec3(0,1,0))
            z_axis_world = gizmo_world_quat.xform(Vec3(0,0,1))

            x_end = gizmo_world_pos + x_axis_world * self.axis_length
            y_end = gizmo_world_pos + y_axis_world * self.axis_length
            z_end = gizmo_world_pos + z_axis_world * self.axis_length


            # 计算各轴端点的世界坐标
            # x_end = gizmo_world_pos + Vec3(self.axis_length, 0, 0)
            # y_end = gizmo_world_pos + Vec3(0, self.axis_length, 0)
            # z_end = gizmo_world_pos + Vec3(0, 0, self.axis_length)

            # 将3D坐标投影到屏幕坐标
            def worldToScreen(worldPos):
                try:
                    # 转换为相机坐标系
                    camPos = self.world.cam.getRelativePoint(self.world.render, worldPos)

                    # 检查点是否在相机前方
                    if camPos.getY() <= 0:
                        return None

                    # 使用相机lens进行投影
                    screenPos = Point2()
                    lens = self.world.cam.node().getLens()

                    if lens.project(camPos, screenPos):
                        # 获取准确的窗口尺寸
                        winWidth, winHeight = self.world.getWindowSize()

                        # 转换为像素坐标
                        winX = (screenPos.x + 1) * 0.5 * winWidth
                        winY = (1 - screenPos.y) * 0.5 * winHeight
                        return (winX, winY)
                    return None
                except:
                    return None

            # 获取各坐标轴的屏幕投影
            gizmo_screen = worldToScreen(gizmo_world_pos)
            x_screen = worldToScreen(x_end)
            y_screen = worldToScreen(y_end)
            z_screen = worldToScreen(z_end)

            # 如果坐标轴中心不在屏幕内，返回None
            if not gizmo_screen:
                return None

            # 设置检测阈值
            click_threshold = 35  # 统一使用25像素的检测阈值

            # 更准确的点到线段距离计算方法
            def distanceToLineSegment(mousePos, start, end):
                import math
                mx, my = mousePos
                x1, y1 = start
                x2, y2 = end

                # 线段向量
                dx = x2 - x1
                dy = y2 - y1

                # 线段长度平方
                length_sq = dx * dx + dy * dy

                if length_sq == 0:
                    # 线段退化为点
                    return math.sqrt((mx - x1) ** 2 + (my - y1) ** 2)

                # 投影参数
                t = max(0, min(1, ((mx - x1) * dx + (my - y1) * dy) / length_sq))

                # 投影点坐标
                proj_x = x1 + t * dx
                proj_y = y1 + t * dy

                # 返回点到投影点的距离
                return math.sqrt((mx - proj_x) ** 2 + (my - proj_y) ** 2)

            mouse_pos = (mouseX, mouseY)

            # 检测各个轴 - 按优先级检测（Z > X > Y）
            axes_to_check = [
                ("z", z_screen),
                ("x", x_screen),
                ("y", y_screen)
            ]

            for axis_name, axis_end in axes_to_check:
                if axis_end:
                    distance = distanceToLineSegment(mouse_pos, gizmo_screen, axis_end)
                    if distance < click_threshold:
                        return axis_name

            # 如果没有检测到，返回None
            return None

        except Exception as e:
            # 静默处理错误，避免频繁输出
            return None

    def updateGizmoHighlight(self, mouseX, mouseY):
        """更新坐标轴高亮状态"""
        if not self.gizmo or self.isDraggingGizmo:
            self._resetCursor()
            return

        # 使用碰撞检测方法
        #hoveredAxis = self.detectGizmoAxisWithCollision(mouseX, mouseY)
        hoveredAxis = self.detectGizmoAxisAtMouse(mouseX, mouseY)

        # 简化稳定性检测逻辑
        if not hasattr(self, '_last_detected_axis'):
            self._last_detected_axis = None

        # 如果检测结果发生变化，立即更新高亮
        if hoveredAxis != self._last_detected_axis:
            # 更新轴的高亮状态
            if hoveredAxis != self.gizmoHighlightAxis:
                # 恢复之前高亮的轴
                if self.gizmoHighlightAxis:
                    self.setGizmoAxisColor(self.gizmoHighlightAxis, self.gizmo_colors[self.gizmoHighlightAxis])

                # 高亮新的轴
                if hoveredAxis:
                    self.setGizmoAxisColor(hoveredAxis, self.gizmo_highlight_colors[hoveredAxis])
                    self._setCursor("pointing_hand")
                else:
                    # 如果没有悬停在任何轴上，确保所有轴都恢复原始颜色
                    for axis_name in ["x", "y", "z"]:
                        if axis_name != self.dragGizmoAxis:  # 不要改变正在拖拽的轴的颜色
                            self.setGizmoAxisColor(axis_name, self.gizmo_colors[axis_name])
                    self._resetCursor()

                self.gizmoHighlightAxis = hoveredAxis

            self._last_detected_axis = hoveredAxis
        elif hoveredAxis is None:
            self._resetCursor()

    def _detectHoveredAxis(self, mouseX, mouseY):
        """检测鼠标悬停的轴 - 提取为独立方法"""
        # 将原来 updateGizmoHighlight 中的检测逻辑移到这里
        # ... 你原来的检测代码 ...
        pass

    def _updateAxisHighlight(self, hoveredAxis):
        """更新轴高亮状态 - 确保原子性操作"""
        # 恢复之前高亮的轴
        if self.gizmoHighlightAxis:
            self.setGizmoAxisColor(self.gizmoHighlightAxis, self.gizmo_colors[self.gizmoHighlightAxis])

            # 高亮新的轴
        if hoveredAxis:
            self.setGizmoAxisColor(hoveredAxis, self.gizmo_highlight_colors[hoveredAxis])

        self.gizmoHighlightAxis = hoveredAxis

    # ==================== 拖拽变换 ====================

    def startGizmoDrag(self, axis, mouseX, mouseY):
        """开始坐标轴拖拽"""
        try:
            # 确保状态正确初始化
            if not self.gizmoTarget:
                print("开始拖拽失败: 没有拖拽目标")
                return
            if not self.gizmo:
                print("开始拖拽失败: 没有坐标轴")
                return

            self.isDraggingGizmo = True

            # 使用当前高亮的轴，如果有的话；否则使用传入的轴
            if self.gizmoHighlightAxis:
                self.dragGizmoAxis = self.gizmoHighlightAxis
            elif axis and axis in self.gizmo_colors:
                self.dragGizmoAxis = axis
            else:
                # 如果没有明确指定轴，尝试通过鼠标位置检测
                self.dragGizmoAxis = self.detectGizmoAxisAtMouse(mouseX, mouseY)

            # 启动拖拽更新任务
            if hasattr(self.world, 'taskMgr') and self.world.taskMgr:
                self.world.taskMgr.add(self._dragUpdateTask, "gizmoDragUpdate")

            # 如果仍然无法确定拖拽轴，则取消拖拽
            if not self.dragGizmoAxis:
                print("开始拖拽失败: 无法确定拖拽轴")
                self.isDraggingGizmo = False
                return

            self.dragStartMousePos = (mouseX, mouseY)

            light_object = self.gizmoTarget.getPythonTag("rp_light_object")
            if light_object:
                self.gizmoTargetStartPos = Point3(light_object.pos)
            else:
                self.gizmoTargetStartPos = self.gizmoTarget.getPos()

            # 保存开始拖拽时目标节点的位置和坐标轴的位置
            #self.gizmoTargetStartPos = self.gizmoTarget.getPos()
            self.gizmoStartPos = self.gizmo.getPos(self.world.render)  # 坐标轴的世界位置

            # 添加对缩放的支持：保存初始缩放值
            if self.world.tool_manager.isScaleTool():
                self.gizmoTargetStartScale = self.gizmoTarget.getScale()
            elif self.world.tool_manager.isRotateTool():
                self.gizmoTargetStartHpr = self.gizmoTarget.getHpr()

            # 确保正在拖动的轴保持高亮状态
            if self.dragGizmoAxis and self.dragGizmoAxis in self.gizmo_colors:
                # 先将所有轴恢复为正常颜色
                for axis_name in self.gizmo_colors.keys():
                    if axis_name != self.dragGizmoAxis:
                        self.setGizmoAxisColor(axis_name, self.gizmo_colors[axis_name])

                # 然后将当前拖动的轴设置为高亮颜色
                self.setGizmoAxisColor(self.dragGizmoAxis, self.gizmo_highlight_colors[self.dragGizmoAxis])
            # elif axis and axis in self.gizmo_colors:
            #     for axis_name in self.gizmo_colors.keys():
            #         if axis_name != axis:
            #             self.setGizmoAxisColor(axis_name, self.gizmo_colors[axis_name])
            #
            #     self.setGizmoAxisColor(axis, self.gizmo_highlight_colors[axis])
            #     self.dragGizmoAxis = axis
            #
            # self.gizmoHighlightAxis = self.dragGizmoAxis
                # 设置拖拽光标
                if self.dragGizmoAxis == "x":
                    self._setCursor("size_all")  # 水平调整光标
                elif self.dragGizmoAxis == "y":
                    self._setCursor("size_all")  # 垂直调整光标
                elif self.dragGizmoAxis == "z":
                    self._setCursor("size_all")  # 全向调整光标

            print(
                f"开始拖拽 {self.dragGizmoAxis} 轴 - 目标起始位置: {self.gizmoTargetStartPos}, 坐标轴位置: {self.gizmoStartPos}, 鼠标: ({mouseX}, {mouseY})")

        except Exception as e:
            print(f"开始坐标轴拖拽失败: {str(e)}")
            import traceback
            traceback.print_exc()

    def updateGizmoDrag(self, mouseX, mouseY):
        """更新坐标轴拖拽 - 使用正确的坐标系变换，支持旋转后的子节点拖拽"""
        try:
            # 添加详细的状态检查和调试信息
            if not self.isDraggingGizmo:
                print("拖拽更新失败: 不在拖拽状态")
                return
            if not self.gizmoTarget:
                print("拖拽更新失败: 没有拖拽目标")
                return
            if not hasattr(self, 'dragStartMousePos') or not self.dragStartMousePos:
                print("拖拽更新失败: 没有拖拽起始位置")
                return
            if not hasattr(self, 'gizmoTargetStartPos') or not self.gizmoTargetStartPos:
                print("拖拽更新失败: 没有目标起始位置")
                return
            if not hasattr(self, 'gizmoStartPos') or not self.gizmoStartPos:
                print("拖拽更新失败: 没有坐标轴起始位置")
                return

            is_scale_tool = self.world.tool_manager.isScaleTool() if self.world.tool_manager else False
            is_rotate_tool = self.world.tool_manager.isRotateTool() if self.world.tool_manager else False

            is_gui_element = (hasattr(self.gizmoTarget,'getTag') and
                              self.gizmoTarget.getTag("is_gui_element") == "1")


            # 计算鼠标移动距离（屏幕像素）
            mouseDeltaX = mouseX - self.dragStartMousePos[0]
            mouseDeltaY = mouseY - self.dragStartMousePos[1]

            if is_scale_tool:
                scale_factor = 1.0 + (mouseDeltaX + mouseDeltaY) * 0.01

                scale_factor = max(0.001, scale_factor)

                start_scale = getattr(self,'gizmoTargetStartScale',Vec3(1,1,1))

                if is_gui_element:
                    if self.dragGizmoAxis == "x":
                        new_scale = Vec3(start_scale.x * scale_factor,start_scale.y,start_scale.z)
                    elif self.dragGizmoAxis == "y":
                        new_scale = Vec3(start_scale.x, start_scale.y * scale_factor, start_scale.z)
                    elif self.dragGizmoAxis == "z":
                        new_scale = Vec3(start_scale.x, start_scale.y, start_scale.z * scale_factor)
                    else:
                        new_scale = Vec3(start_scale.x * scale_factor,
                                         start_scale.y * scale_factor,
                                         start_scale.z * scale_factor)
                else:
                    # 普通3D模型的缩放处理
                    if self.dragGizmoAxis == "x":
                        new_scale = Vec3(start_scale.x * scale_factor, start_scale.y, start_scale.z)
                    elif self.dragGizmoAxis == "y":
                        new_scale = Vec3(start_scale.x, start_scale.y * scale_factor, start_scale.z)
                    elif self.dragGizmoAxis == "z":
                        z_scale_factor = 1.0 - (mouseDeltaX + mouseDeltaY) * 0.01
                        new_scale = Vec3(start_scale.x, start_scale.y, start_scale.z * z_scale_factor)
                    else:
                        new_scale = Vec3(start_scale.x * scale_factor,
                                         start_scale.y * scale_factor,
                                         start_scale.z * scale_factor)

                new_scale = Vec3(
                    max(0.001,new_scale.x),
                    max(0.001,new_scale.y),
                    max(0.001,new_scale.z)
                )

                # 应用新缩放值
                self.gizmoTarget.setScale(new_scale)
                if hasattr(self.world, 'property_panel') and self.world.property_panel:
                    self.world.property_panel.refreshModelValues(self.gizmoTarget)
                return
            elif is_rotate_tool:
                rotation_speed = 0.5
                rotation_amount = (mouseDeltaX + mouseDeltaY) * rotation_speed
                start_hpr = getattr(self,'gizmoTargetStartHpr',self.gizmoTarget.getHpr())

                if self.dragGizmoAxis == "x":
                    new_hpr = Vec3(start_hpr.x+rotation_amount,start_hpr.y,start_hpr.z)
                elif self.dragGizmoAxis == "y":
                    new_hpr = Vec3(start_hpr.x,start_hpr.y-rotation_amount,start_hpr.z)
                elif self.dragGizmoAxis == "z":
                    new_hpr = Vec3(start_hpr.x,start_hpr.y,start_hpr.z+rotation_amount)
                else:
                    # 默认绕所有轴旋转
                    new_hpr = Vec3(start_hpr.x + rotation_amount,
                                   start_hpr.y + rotation_amount,
                                   start_hpr.z + rotation_amount)
                self.gizmoTarget.setHpr(new_hpr)
                if hasattr(self.world, 'property_panel') and self.world.property_panel:
                    self.world.property_panel.refreshModelValues(self.gizmoTarget)
                return

            # 使用坐标轴的实际位置而不是目标节点位置来计算屏幕投影
            gizmo_world_pos = self.gizmoStartPos

            # 【关键修复】：获取正确的轴向量，考虑父节点的旋转
            # 检查目标节点是否有父节点
            parent_node = self.gizmoTarget.getParent()

            # 计算轴向量在正确坐标系中的方向
            if self.dragGizmoAxis == "x":
                # 在局部坐标系中的X轴方向
                local_axis_vector = Vec3(1, 0, 0)
            elif self.dragGizmoAxis == "y":
                # 在局部坐标系中的Y轴方向
                local_axis_vector = Vec3(0, 1, 0)
            elif self.dragGizmoAxis == "z":
                # 在局部坐标系中的Z轴方向
                local_axis_vector = Vec3(0, 0, 1)
            else:
                print(f"拖拽更新失败: 未知轴类型 {self.dragGizmoAxis}")
                return

            world_axis_vector = local_axis_vector

            if parent_node and parent_node != self.world.render:
                try:
                    #获取变换矩阵
                    transfrom_mat = parent_node.getMat(self.world.render)
                    if transfrom_mat.is_identity() or self._isMatrixValid(transfrom_mat):
                        world_axis_vector = transfrom_mat.xformVec(local_axis_vector)
                    else:
                        print("警告: 检测到无效变换矩阵，使用默认轴向量")
                except Exception as e:
                    print(f"变换计算出错: {e}，使用默认轴向量")
            else:
                world_axis_vector = local_axis_vector

            # 确定轴向量的变换上下文
            # if parent_node and parent_node != self.world.render:
            #     transform_mat = parent_node.getMat(self.world.render)
            #     world_axis_vector = transform_mat.xformVec(local_axis_vector)
            # else:
            #     world_axis_vector = local_axis_vector

            #axis_end = gizmo_world_pos + world_axis_vector

            # 投影到屏幕空间
            def worldToScreen(worldPos):
                try:
                    camPos = self.world.cam.getRelativePoint(self.world.render, worldPos)
                    if camPos.getY() <= 0:
                        return None

                    screenPos = Point2()
                    if self.world.cam.node().getLens().project(camPos, screenPos):
                        winWidth, winHeight = self.world.getWindowSize()
                        winX = (screenPos.x + 1) * 0.5 * winWidth
                        winY = (1 - screenPos.y) * 0.5 * winHeight
                        return (winX, winY)
                    return None
                except Exception as e:
                    print(f"世界坐标转屏幕坐标失败: {e}")
                    return None
            axis_start_screen = worldToScreen(gizmo_world_pos)
            axis_end_world = gizmo_world_pos + world_axis_vector
            axis_end_screen = worldToScreen(axis_end_world)

            if not axis_start_screen or not axis_end_screen:
                print("拖拽更新失败: 无法获取轴线屏幕坐标")
                return

            screen_axis_dir = (
                axis_end_screen[0] - axis_start_screen[0],
                axis_end_screen[1] - axis_start_screen[1]
            )

            # 归一化屏幕轴方向
            import math
            length = math.sqrt(screen_axis_dir[0]**2 + screen_axis_dir[1]**2)
            if length > 0:
                #screen_axis_dir = (screen_axis_dir[0] / length, screen_axis_dir[1] / length)
                screen_axis_dir = (
                    screen_axis_dir[0] / length,
                    screen_axis_dir[1] / length
                )

            else:
                print("拖拽更新失败: 屏幕轴方向长度为0")
                return

            # 将鼠标移动投影到轴方向上
            projected_distance = (mouseDeltaX * screen_axis_dir[0] +
                                mouseDeltaY * screen_axis_dir[1])

            cam_pos = self.world.cam.getPos(self.world.render)
            distance_to_object = (cam_pos - gizmo_world_pos).length()

            lens = self.world.cam.node().getLens()
            fov = lens.getFov()[0]
            winWidth,winHeight = self.world.getWindowSize()

            pixels_to_world_units = (2*distance_to_object*math.tan(math.radians(fov/2)))/winWidth

            movement_distance = projected_distance * pixels_to_world_units

            total_scale_factor = 1.0
            current_node = self.gizmoTarget.getParent()

            while current_node and current_node != self.world.render:
                try:
                    if not current_node.isEmpty():
                        node_scale = current_node.getScale()
                        if node_scale.x > 0 and node_scale.y >0 and node_scale.z >0 :
                            avg_scale = (node_scale.x + node_scale.y + node_scale.z)/3.0
                            total_scale_factor *= avg_scale
                        #avg_scale = (node_scale.x+node_scale.y + node_scale.z) / 3.0
                        #total_scale_factor *= avg_scale
                        current_node = current_node.getParent()
                    else:
                        break
                except:
                    break


            if total_scale_factor > 0:
                movement_distance = movement_distance / total_scale_factor

            currentPos = self.gizmoTargetStartPos

            # 根据拖拽的轴，只修改对应的坐标分量
            if self.dragGizmoAxis == "x":
                newPos = Vec3(currentPos.x + movement_distance, currentPos.y, currentPos.z)
                #print(f"X轴移动：{currentPos.x} -> {newPos.x}")
            elif self.dragGizmoAxis == "y":
                newPos = Vec3(currentPos.x, currentPos.y + movement_distance, currentPos.z)
                #print(f"Y轴移动：{currentPos.y} -> {newPos.y}")
            elif self.dragGizmoAxis == "z":
                newPos = Vec3(currentPos.x, currentPos.y, currentPos.z + movement_distance)
                #print(f"Z轴移动：{currentPos.z} -> {newPos.z}")
            else:
                print(f"未知轴: {self.dragGizmoAxis}")
                return
            
            # 应用新位置到目标节点
            light_object = self.gizmoTarget.getPythonTag("rp_light_object")
            if light_object:
                light_object.pos = newPos
                self.gizmoTarget.setPos(newPos)
                print(f"🔄 光源拖拽移动: {currentPos} -> {newPos}")
            else:
                self.gizmoTarget.setPos(newPos)
                print(f"🔄 节点拖拽移动: {currentPos} -> {newPos} (轴: {self.dragGizmoAxis}, 距离: {movement_distance:.3f})")
                
            # 更新属性面板
            if hasattr(self.world, 'property_panel') and self.world.property_panel:
                self.world.property_panel.refreshModelValues(self.gizmoTarget)
            
            # 更新坐标轴位置 - 计算新的中心位置
            minPoint = Point3()
            maxPoint = Point3()
            if self.gizmoTarget.calcTightBounds(minPoint, maxPoint, self.world.render):
                center = Point3((minPoint.x + maxPoint.x) * 0.5,
                              (minPoint.y + maxPoint.y) * 0.5,
                              (minPoint.z + maxPoint.z) * 0.5)
                self.gizmo.setPos(center)

            # 实时更新属性面板
            if hasattr(self.world, 'property_panel') and self.world.property_panel:
                self.world.property_panel.refreshModelValues(self.gizmoTarget)

            # 每次拖拽都输出调试信息（但限制频率）
            if not hasattr(self, '_last_drag_debug_time'):
                self._last_drag_debug_time = 0

            import time
            current_time = time.time()
            if current_time - self._last_drag_debug_time > 0.1:  # 每0.1秒最多输出一次
                #print(f"拖拽更新成功 - 轴:{self.dragGizmoAxis}, 比例:{scale_factor:.6f}, 投影:{projected_distance:.2f}")
                self._last_drag_debug_time = current_time

        except Exception as e:
            print(f"更新坐标轴拖拽失败: {str(e)}")
            import traceback
            traceback.print_exc()

    def _isMatrixValid(self, matrix):
        """检查矩阵是否有效，替代 isSingular 方法"""
        try:
            # 检查矩阵元素是否为有效数字
            for i in range(4):
                for j in range(4):
                    element = matrix.getCell(i, j)
                    # 检查是否为 NaN 或无穷大
                    if str(element) == 'nan' or str(element) == 'inf' or str(element) == '-inf':
                        return False
                    # 检查是否过大
                    if abs(element) > 1e10:
                        return False
            return True
        except:
            return False

    def stopGizmoDrag(self):
        """停止坐标轴拖拽并创建撤销命令"""
        print(f"停止坐标轴拖拽 - 轴: {self.dragGizmoAxis}")
        
        # 移除拖拽更新任务
        if hasattr(self.world, 'taskMgr') and self.world.taskMgr:
            self.world.taskMgr.remove("gizmoDragUpdate")
        
        # 创建撤销命令
        if hasattr(self.world,'command_manager') and self.world.command_manager and self.gizmoTarget:
            try:
                # 检查是否是移动操作
                if (hasattr(self,'gizmoTargetStartPos') and self.gizmoTargetStartPos):
                    current_pos = self.gizmoTarget.getPos()
                    if (abs(current_pos.x-self.gizmoTargetStartPos.x)>0.001 or
                        abs(current_pos.y-self.gizmoTargetStartPos.y)>0.001 or
                        abs(current_pos.z-self.gizmoTargetStartPos.z)>0.001):
                        from core.Command_System import MoveNodeCommand, MoveLightCommand
                        
                        light_object = self.gizmoTarget.getPythonTag("rp_light_object")
                        if light_object:
                            command = MoveLightCommand(self.gizmoTarget,self.gizmoTargetStartPos,current_pos,light_object)
                        else:
                            command = MoveNodeCommand(self.gizmoTarget,self.gizmoTargetStartPos,current_pos)
                        self.world.command_manager.execute_command(command)
                        print(f"创建移动命令: {self.gizmoTargetStartPos} -> {current_pos}")
                
                # 检查是否是缩放操作
                if (hasattr(self, 'gizmoTargetStartScale') and self.gizmoTargetStartScale):
                    current_scale = self.gizmoTarget.getScale()
                    if (abs(current_scale.x - self.gizmoTargetStartScale.x) > 0.001 or
                        abs(current_scale.y - self.gizmoTargetStartScale.y) > 0.001 or
                        abs(current_scale.z - self.gizmoTargetStartScale.z) > 0.001):
                        from core.Command_System import ScaleNodeCommand
                        command = ScaleNodeCommand(self.gizmoTarget, self.gizmoTargetStartScale, current_scale)
                        self.world.command_manager.execute_command(command)
                        print(f"创建缩放命令: {self.gizmoTargetStartScale} -> {current_scale}")
                
                # 检查是否是旋转操作
                if (hasattr(self, 'gizmoTargetStartHpr') and self.gizmoTargetStartHpr):
                    current_hpr = self.gizmoTarget.getHpr()
                    if (abs(current_hpr.x - self.gizmoTargetStartHpr.x) > 0.001 or
                        abs(current_hpr.y - self.gizmoTargetStartHpr.y) > 0.001 or
                        abs(current_hpr.z - self.gizmoTargetStartHpr.z) > 0.001):
                        from core.Command_System import RotateNodeCommand
                        command = RotateNodeCommand(self.gizmoTarget, self.gizmoTargetStartHpr, current_hpr)
                        self.world.command_manager.execute_command(command)
                        print(f"创建旋转命令: {self.gizmoTargetStartHpr} -> {current_hpr}")
                        
            except Exception as e:
                print(f"创建撤销命令时出错: {e}")
        
        # 恢复所有轴的颜色
        for axis_name in ["x", "y", "z"]:
            self.setGizmoAxisColor(axis_name, self.gizmo_colors[axis_name])

        # 重置拖拽状态
        self.isDraggingGizmo = False
        self.dragGizmoAxis = None
        self.dragStartMousePos = None
        # 清理拖拽状态，下次拖拽开始时重新设置
        self.gizmoTargetStartPos = None
        self.gizmoTargetStartScale = None
        self.gizmoTargetStartHpr = None
        self.gizmoStartPos = None

    def _dragUpdateTask(self, task):
        """拖拽更新任务 - 持续更新拖拽状态"""
        try:
            if not self.isDraggingGizmo:
                return task.done
            
            # 检查鼠标是否仍然按下
            if not self.world.mouseWatcherNode.hasMouse():
                return task.cont
            
            # 获取当前鼠标位置
            mouse_x = self.world.mouseWatcherNode.getMouseX()
            mouse_y = self.world.mouseWatcherNode.getMouseY()
            
            # 转换为窗口坐标
            winWidth, winHeight = self.world.getWindowSize()
            window_x = (mouse_x + 1) * 0.5 * winWidth
            window_y = (1 - mouse_y) * 0.5 * winHeight
            
            # 调用拖拽更新
            self.updateGizmoDrag(window_x, window_y)
            
            return task.cont
            
        except Exception as e:
            print(f"拖拽更新任务错误: {e}")
            return task.done
        if hasattr(self, 'gizmoTargetStartScale'):
            delattr(self, 'gizmoTargetStartScale')
        if hasattr(self, 'gizmoTargetStartHpr'):
            delattr(self, 'gizmoTargetStartHpr')

        # 重置高亮轴
        self.gizmoHighlightAxis = None
        self._resetCursor()
    # ==================== 选择管理 ====================

    def updateSelection(self, nodePath):
        try:
            if self.selectedNode == nodePath:
                return
            #print(f"\n=== 更新选择状态 ===")

            # 如果正在删除节点，避免更新选择
            if hasattr(self, '_deleting_node') and self._deleting_node:
                print("正在删除节点，跳过选择更新")
                #print("=== 选择状态更新完成 ===\n")
                return

            node_name = "None"
            if nodePath and not nodePath.isEmpty():
                node_name = nodePath.getName()
            #print(f"新选择的节点: {node_name}")

            self.selectedNode = nodePath
            # 添加兼容性属性
            self.selectedObject = nodePath

            if nodePath and not nodePath.isEmpty():
                node_name = nodePath.getName()
                #print(f"开始为节点 {node_name} 创建选择框和坐标轴...")

                # 创建选择框
                #print("创建选择框...")
                self.createSelectionBox(nodePath)
                if self.selectionBox:
                    box_name = "Unknown"
                    if self.selectionBox and not self.selectionBox.isEmpty():
                        box_name = self.selectionBox.getName()
                    #print(f"✓ 选择框创建成功: {box_name}")
                else:
                    print("× 选择框创建失败")

                # 创建坐标轴
                #print("创建坐标轴...")
                self.createGizmo(nodePath)
                if self.gizmo:
                    gizmo_name = "Unknown"
                    if self.gizmo and not self.gizmo.isEmpty():
                        gizmo_name = self.gizmo.getName()
                    #print(f"✓ 坐标轴创建成功: {gizmo_name}")
                else:
                    print("× 坐标轴创建失败")

            else:
                print("清除选择...")
                self.clearSelectionBox()
                self.clearGizmo()
                print("✓ 取消选择")

                #当取消选择时，同步清空树形控件的选中状态
                if (hasattr(self.world,'interface_manager')and
                     self.world.interface_manager and
                     self.world.interface_manager.treeWidget):
                    self.world.interface_manager.treeWidget.setCurrentItem(None)
                    print("✓ 树形控件选中状态已清空")

            #print("=== 选择状态更新完成 ===\n")
        except Exception as e:
            print(f"更新选择状态失败{str(e)}")
            import traceback
            traceback.print_exc()

    def _reparentTreeItem(self, item, new_parent_item):
        """将树项重新父化到新的父项下"""
        if not item or not new_parent_item:
            return

        # 从当前父项中移除
        current_parent = item.parent()
        if current_parent:
            current_parent.removeChild(item)
        else:
            # 如果是顶级项
            index = self.indexOfTopLevelItem(item)
            if index >= 0:
                self.takeTopLevelItem(index)

        # 添加到新父项
        new_parent_item.addChild(item)

    # def _updateSelectionVisuals(self, nodePath):
    #     """更新选择的视觉效果（选择框和坐标轴）"""
    #     try:
    #         if nodePath and not nodePath.isEmpty():
    #             node_name = nodePath.getName()
    #             print(f"开始为节点 {node_name} 创建选择框和坐标轴...")
    #
    #             # 创建选择框
    #             print("创建选择框...")
    #             self.createSelectionBox(nodePath)
    #             if self.selectionBox:
    #                 box_name = "Unknown"
    #                 if self.selectionBox and not self.selectionBox.isEmpty():
    #                     box_name = self.selectionBox.getName()
    #                 print(f"✓ 选择框创建成功: {box_name}")
    #             else:
    #                 print("× 选择框创建失败")
    #
    #             # 创建坐标轴
    #             print("创建坐标轴...")
    #             self.createGizmo(nodePath)
    #             if self.gizmo:
    #                 gizmo_name = "Unknown"
    #                 if self.gizmo and not self.gizmo.isEmpty():
    #                     gizmo_name = self.gizmo.getName()
    #                 print(f"✓ 坐标轴创建成功: {gizmo_name}")
    #             else:
    #                 print("× 坐标轴创建失败")
    #
    #             print(f"✓ 选中了节点: {node_name}")
    #         else:
    #             print("清除选择...")
    #             self.clearSelectionBox()
    #             self.clearGizmo()
    #             print("✓ 取消选择")
    #
    #     except Exception as e:
    #         print(f"更新选择视觉效果失败: {e}")

    def getSelectedNode(self):
        """获取当前选中的节点"""
        return self.selectedNode

    def hasSelection(self):
        """检查是否有选中的节点"""
        return self.selectedNode is not None

    def checkAndClearIfTargetDeleted(self):
        if (self.gizmoTarget and self.gizmoTarget.isEmpty()):
            self.clearGizmo()

        if (self.selectionBoxTarget and self.selectionBoxTarget.isEmpty()):
            self.clearSelectionBox()

    def setupGizmoCollision(self):
        if not self.gizmo or not self.gizmoXAxis or not self.gizmoYAxis or not self.gizmoZAxis:
            return

        # 清除现有的碰撞节点
        for axis_name in ["x", "y", "z"]:
            axis_node = getattr(self, f"gizmo{axis_name.upper()}Axis")
            if axis_node:
                # 查找并移除所有现有的碰撞节点
                collision_nodes = axis_node.findAllMatches("**/gizmo_collision_*")
                for collision_node in collision_nodes:
                    collision_node.removeNode()

        # 为每个轴创建碰撞体
        self.createAxisCollision("x", self.gizmoXAxis)
        self.createAxisCollision("y", self.gizmoYAxis)
        self.createAxisCollision("z", self.gizmoZAxis)

    def createAxisCollision(self, axis_name, axis_node):
        # 为单个轴创建碰撞体
        try:
            handle_node = axis_node.find(f"{axis_name}_handle")
            if not handle_node or handle_node.isEmpty():
                children = axis_node.getChildren()
                if children.getNumPaths() > 0:
                    handle_node = children[0]
                else:
                    print(f"警告: 未找到 {axis_name} 轴的 handle 节点")
                    return

            collision_node = CollisionNode(f"gizmo_collision_{axis_name}")
            collision_node.setIntoCollideMask(BitMask32.bit(1))  # 设置为into对象
            collision_node.setFromCollideMask(BitMask32.allOff())  # 不作为from对象

            # 调整碰撞尺寸以匹配实际的轴长度和坐标轴缩放
            scale_factor = self.gizmo.getScale().x if self.gizmo else 1.0
            axis_length = 2.0 * scale_factor
            radius = 0.3 * scale_factor

            # 根据轴的类型创建合适的碰撞体
            if axis_name == "x":
                capsule = CollisionCapsule(
                    Point3(0, 0, 0),
                    Point3(axis_length, 0, 0),
                    radius
                )
                collision_node.addSolid(capsule)
            elif axis_name == "y":
                capsule = CollisionCapsule(
                    Point3(0, 0, 0),
                    Point3(0, axis_length, 0),
                    radius
                )
                collision_node.addSolid(capsule)
            elif axis_name == "z":
                capsule = CollisionCapsule(
                    Point3(0, 0, 0),
                    Point3(0, 0, axis_length),
                    radius
                )
                collision_node.addSolid(capsule)

            # 将碰撞节点附加到handle节点，使其与可视化几何体保持一致
            collision_np = handle_node.attachNewNode(collision_node)

            # 设置标签以便识别
            collision_np.setTag("gizmo_axis", axis_name)
            collision_np.setTag("pickable", "1")

            collision_np.hide()  # 隐藏碰撞体，只用于检测

            #print(f"✓ 成功创建 {axis_name} 轴碰撞体")

        except Exception as e:
            print(f"创建{axis_name}轴碰撞体失败: {e}")
            import traceback
            traceback.print_exc()

    def detectGizmoAxisWithCollision(self, mouseX, mouseY):
        # 使用碰撞体检测鼠标是否悬停在坐标轴上
        if not self.gizmo:
            return None

        try:
            ray = CollisionRay()

            win_width, win_height = self.world.getWindowSize()

            mouse_x_ndc = (mouseX / win_width) * 2.0 - 1.0
            mouse_y_ndc = 1.0 - (mouseY / win_height) * 2.0

            ray.setFromLens(self.world.cam.node(), mouse_x_ndc, mouse_y_ndc)

            traverser = CollisionTraverser("gizmo_traverser")
            handler = CollisionHandlerQueue()

            # 创建射线节点
            ray_node = CollisionNode('mouseRay')
            ray_node.addSolid(ray)
            ray_node.setFromCollideMask(BitMask32.bit(1))  # 射线作为from对象
            ray_node.setIntoCollideMask(BitMask32.allOff())  # 射线不作为into对象
            ray_np = self.world.render.attachNewNode(ray_node)

            # 为所有轴的碰撞体设置正确的掩码并添加到遍历器
            collision_found = False
            for axis_name in ["x", "y", "z"]:
                axis_node = getattr(self, f"gizmo{axis_name.upper()}Axis")
                if axis_node:
                    collision_node_path = axis_node.find("**/gizmo_collision_*")
                    if not collision_node_path.isEmpty():
                        collision_node = collision_node_path.node()
                        collision_node.setFromCollideMask(BitMask32.allOff())  # 碰撞体不作为from对象
                        collision_node.setIntoCollideMask(BitMask32.bit(1))  # 碰撞体作为into对象
                        collision_found = True

            if not collision_found:
                ray_np.removeNode()
                return None

            # 执行碰撞检测 - 这里是关键修复点
            traverser.addCollider(ray_np, handler)
            traverser.traverse(self.world.render)

            ray_np.removeNode()

            # 检查是否有碰撞
            if handler.getNumEntries() > 0:
                handler.sortEntries()
                closest_entry = handler.getEntry(0)

                # 获取碰撞的对象
                collided_object = closest_entry.getIntoNodePath()
                axis_tag = collided_object.getTag("gizmo_axis")

                if axis_tag in ["x", "y", "z"]:
                    return axis_tag

            return None

        except Exception as e:
            print(f"使用碰撞体检测坐标轴失败: {e}")
            import traceback
            traceback.print_exc()
            return None

    def debugGizmoCollision(self):
        print("===碰撞体调试信息===")
        for axis_name in ["x","y","z"]:
            axis_node = getattr(self,f"gizmo{axis_name.upper()}Axis")
            if axis_node:
                handle_node = axis_node.find(f"{axis_name}_handle")
                collision_node = axis_node.find("**/gizmo_collision_*")
                print(f"{axis_name.upper()}轴:")
                print(f"  - 轴节点: {axis_node}")
                print(f"  - Handle节点: {handle_node}")
                print(f"  - 碰撞节点: {collision_node}")
                if not collision_node.isEmpty():
                    print(f"  - 碰撞体标签: {collision_node.getTag('gizmo_axis')}")
            else:
                print(f"{axis_name.upper()}轴节点不存在")

    def focusCameraOnSelectedNodeAdvanced(self):
        """高级版的摄像机聚焦功能，包含平滑动画效果"""
        try:
            if not self.selectedNode or self.selectedNode.isEmpty():
                print("没有选中的节点，无法聚焦")
                return False

            # 获取选中节点的边界框
            minPoint = Point3()
            maxPoint = Point3()

            if not self.selectedNode.calcTightBounds(minPoint, maxPoint, self.world.render):
                print("无法计算选中节点的边界框，使用节点为位置作为替代方案")
                node_pos = self.selectedNode.getPos(self.world.render)
                optimal_distance = 10.0
                current_cam_pos = self.world.cam.getPos()
                view_direction = node_pos - current_cam_pos
                if view_direction.length()<0.001:
                    view_direction = Vec3(5,-5,2)
                view_direction.normalize()
                target_cam_pos = node_pos - (view_direction * optimal_distance)

                temp_node =self.world.render.attachNewNode("temp_lookat_target")
                temp_node.setPos(node_pos)
                dummy_cam = self.world.render.attachNewNode("dummy_camera")
                dummy_cam.setPos(target_cam_pos)
                dummy_cam.lookAt(temp_node)
                target_cam_hpr = Vec3(dummy_cam.getHpr())

                temp_node.removeNode()
                dummy_cam.removeNode()

                currrent_cam_pos = Point3(self.world.cam.getPos())
                current_cam_hpr = Vec3(self.world.cam.getHpr())
                self._startCameraFocusAnimation(current_cam_pos,target_cam_pos,current_cam_hpr,target_cam_hpr)
                print(f"开始聚焦到节点(使用位置): {self.selectedNode.getName()}")
                return True

            # 计算节点中心点和大小
            center = Point3(
                (minPoint.x + maxPoint.x) * 0.5,
                (minPoint.y + maxPoint.y) * 0.5,
                (minPoint.z + maxPoint.z) * 0.5
            )

            # 计算节点的对角线长度
            size = (maxPoint - minPoint).length()

            # 如果节点太小，使用默认大小
            if size < 0.01:
                size = 1.0

            # 获取当前摄像机位置和朝向
            current_cam_pos = Point3(self.world.cam.getPos())
            current_cam_hpr = Vec3(self.world.cam.getHpr())

            # 计算观察方向
            view_direction = current_cam_pos - center
            if view_direction.length() < 0.001:
                view_direction = Vec3(5, -5, 2)

            view_direction.normalize()

            # 计算合适的观察距离
            optimal_distance = max(size * 1, 1.0)

            # 计算目标摄像机位置
            target_cam_pos = center + (view_direction * optimal_distance)

            # 计算目标朝向（不直接使用lookAt，而是计算目标HPR）
            # 创建临时节点用于计算目标朝向
            temp_node = self.world.render.attachNewNode("temp_lookat_target")
            temp_node.setPos(center)

            # 创建另一个临时节点用于计算朝向
            dummy_cam = self.world.render.attachNewNode("dummy_camera")
            dummy_cam.setPos(target_cam_pos)
            dummy_cam.lookAt(temp_node)
            target_cam_hpr = Vec3(dummy_cam.getHpr())

            # 清理临时节点
            temp_node.removeNode()
            dummy_cam.removeNode()

            # 使用任务来实现平滑移动动画
            self._startCameraFocusAnimation(current_cam_pos, target_cam_pos,
                                            current_cam_hpr, target_cam_hpr)

            print(f"开始聚焦到节点: {self.selectedNode.getName()}")
            return True

        except Exception as e:
            print(f"高级聚焦功能失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return False

    def _startCameraFocusAnimation(self, start_pos, end_pos, start_hpr, end_hpr):
        """启动摄像机聚焦动画"""
        try:
            # 创建动画任务
            class CameraFocusData:
                def __init__(self, start_pos, end_pos, start_hpr, end_hpr):
                    self.start_pos = Point3(start_pos)  # 确保是Point3类型
                    self.end_pos = Point3(end_pos)  # 确保是Point3类型
                    self.start_hpr = Vec3(start_hpr)  # 确保是Vec3类型
                    self.end_hpr = Vec3(end_hpr)  # 确保是Vec3类型
                    self.elapsed_time = 0.0
                    self.duration = 0.8  # 增加动画持续时间到0.8秒，让动画更平滑

            self._camera_focus_data = CameraFocusData(start_pos, end_pos, start_hpr, end_hpr)

            # 移除之前的任务（如果存在）
            taskMgr.remove("cameraFocusTask")

            # 添加新任务
            taskMgr.add(self._cameraFocusTask, "cameraFocusTask")

        except Exception as e:
            print(f"启动摄像机聚焦动画失败: {e}")

    def _normalizeAngle(self, angle):
        """规范化角度到-180到180度之间"""
        while angle > 180:
            angle -= 360
        while angle < -180:
            angle += 360
        return angle

    def _cameraFocusTask(self, task):
        """摄像机聚焦动画任务"""
        try:
            if not hasattr(self, '_camera_focus_data'):
                return task.done

            data = self._camera_focus_data
            data.elapsed_time += globalClock.getDt()

            # 计算插值因子
            t = min(1.0, data.elapsed_time / data.duration)

            # 使用更平滑的插值函数
            smooth_t = t * t * (3 - 2 * t)  # 平滑步进插值

            # 手动实现lerp功能
            def lerp_point3(start, end, factor):
                return Point3(
                    start.x + (end.x - start.x) * factor,
                    start.y + (end.y - start.y) * factor,
                    start.z + (end.z - start.z) * factor
                )

            # 角度插值需要特殊处理，确保选择最短路径
            def lerp_hpr(start, end, factor):
                # 规范化角度
                start_x = self._normalizeAngle(start.x)
                start_y = self._normalizeAngle(start.y)
                start_z = self._normalizeAngle(start.z)

                end_x = self._normalizeAngle(end.x)
                end_y = self._normalizeAngle(end.y)
                end_z = self._normalizeAngle(end.z)

                # 计算最短旋转路径
                diff_x = self._normalizeAngle(end_x - start_x)
                diff_y = self._normalizeAngle(end_y - start_y)
                diff_z = self._normalizeAngle(end_z - start_z)

                # 插值
                result_x = start_x + diff_x * factor
                result_y = start_y + diff_y * factor
                result_z = start_z + diff_z * factor

                # 再次规范化
                result_x = self._normalizeAngle(result_x)
                result_y = self._normalizeAngle(result_y)
                result_z = self._normalizeAngle(result_z)

                return Vec3(result_x, result_y, result_z)

            # 计算当前位置和朝向
            current_pos = lerp_point3(data.start_pos, data.end_pos, smooth_t)
            current_hpr = lerp_hpr(data.start_hpr, data.end_hpr, smooth_t)

            # 应用到摄像机
            self.world.cam.setPos(current_pos)
            self.world.cam.setHpr(current_hpr)

            # 检查是否完成
            if t >= 1.0:
                if hasattr(self, '_camera_focus_data'):
                    delattr(self, '_camera_focus_data')
                print("摄像机聚焦动画完成")
                return task.done

            return task.cont

        except Exception as e:
            print(f"摄像机聚焦任务出错: {e}")
            import traceback
            traceback.print_exc()
            return task.done

    def _smoothCameraMoveTask(self, task):
        """平滑摄像机移动任务"""
        try:
            if not hasattr(self, '_smooth_camera_move_data'):
                return task.done

            data = self._smooth_camera_move_data
            data.elapsed_time += globalClock.getDt()

            # 计算插值因子
            t = min(1.0, data.elapsed_time / data.duration)

            # 使用平滑插值
            smooth_t = t * t * (3 - 2 * t)

            # 角度插值需要特殊处理
            def lerp_hpr(start, end, factor):
                # 规范化角度
                start_x = self._normalizeAngle(start.x)
                start_y = self._normalizeAngle(start.y)
                start_z = self._normalizeAngle(start.z)

                end_x = self._normalizeAngle(end.x)
                end_y = self._normalizeAngle(end.y)
                end_z = self._normalizeAngle(end.z)

                # 计算最短旋转路径
                diff_x = self._normalizeAngle(end_x - start_x)
                diff_y = self._normalizeAngle(end_y - start_y)
                diff_z = self._normalizeAngle(end_z - start_z)

                # 插值
                result_x = start_x + diff_x * factor
                result_y = start_y + diff_y * factor
                result_z = start_z + diff_z * factor

                return Vec3(result_x, result_y, result_z)

            # 计算当前位置和朝向
            current_pos = Point3(
                data.start_pos.x + (data.end_pos.x - data.start_pos.x) * smooth_t,
                data.start_pos.y + (data.end_pos.y - data.start_pos.y) * smooth_t,
                data.start_pos.z + (data.end_pos.z - data.start_pos.z) * smooth_t
            )

            current_hpr = lerp_hpr(data.start_hpr, data.end_hpr, smooth_t)

            # 应用到摄像机
            self.world.cam.setPos(current_pos)
            self.world.cam.setHpr(current_hpr)

            # 检查是否完成
            if t >= 1.0:
                if hasattr(self, '_smooth_camera_move_data'):
                    delattr(self, '_smooth_camera_move_data')
                print("摄像机平滑移动完成")
                return task.done

            return task.cont

        except Exception as e:
            print(f"平滑摄像机移动任务出错: {e}")
            import traceback
            traceback.print_exc()
            return task.done

    def handleMouseClick(self, nodePath, mouseX=None, mouseY=None):
        """处理鼠标点击事件 - 支持坐标轴双击聚焦"""
        try:
            # 如果正在删除节点，忽略鼠标点击
            if hasattr(self, '_deleting_node') and self._deleting_node:
                print("正在删除节点，忽略鼠标点击")
                return

            import time
            current_time = time.time()

            # 检查是否点击了坐标轴
            is_gizmo_click = False
            target_node = nodePath

            # 判断是否点击了坐标轴
            if (nodePath and hasattr(nodePath, 'getName') and
                    (nodePath.getName().startswith("gizmo") or
                     "gizmo" in nodePath.getName().lower() or
                     (hasattr(nodePath, 'hasTag') and nodePath.hasTag("is_gizmo")))):
                is_gizmo_click = True
                # 如果有选中的模型，使用选中的模型作为聚焦目标
                if self.selectedNode and not self.selectedNode.isEmpty():
                    target_node = self.selectedNode
                print(f"检测到坐标轴点击，使用目标节点: {target_node.getName() if target_node else 'None'}")

            # 检查是否为双击（同一节点且在时间阈值内）
            is_double_click = (self._last_clicked_node == target_node and
                            target_node is not None and
                               current_time - self._last_click_time < self._double_click_threshold)

            if is_double_click:
                # 双击 detected
                node_name = target_node.getName() if target_node else "None"
                print(f"检测到双击节点: {node_name}")

                # 无论是点击模型还是坐标轴，都执行聚焦
                if target_node and not target_node.isEmpty():
                    print(f"双击聚焦到节点: {target_node.getName()}")
                    if self.selectedNode != target_node:
                        self.updateSelection(target_node)
                    else:
                        self.focusCameraOnSelectedNodeAdvanced()
                else:
                    print("双击事件：没有有效的目标节点")

                # 重置状态以避免三击等误触发
                self._last_click_time = 0
                self._last_clicked_node = None
            else:
                # 单击，更新状态
                self._last_click_time = current_time
                self._last_clicked_node = target_node

                # 如果点击的是坐标轴，保持当前选择不变
                if is_gizmo_click:
                    print("坐标轴单击，保持当前选择")
                else:
                    # 正常的单击选择
                    self.updateSelection(nodePath)

        except Exception as e:
            print(f"处理鼠标点击事件失败: {e}")

    def _onDoubleClick(self, nodePath):
        """双击事件处理"""
        try:
            # 获取实际要聚焦的目标节点
            target_node = nodePath

            # 如果是坐标轴，确保使用关联的模型作为目标
            if (nodePath and hasattr(nodePath, 'hasTag') and
                    nodePath.hasTag("is_gizmo")):
                if self.selectedNode and not self.selectedNode.isEmpty():
                    target_node = self.selectedNode
                    print(f"坐标轴双击，聚焦到关联模型: {target_node.getName()}")
                else:
                    print("坐标轴双击，但没有关联的选中模型")
                    return

            if target_node and not target_node.isEmpty():
                print(f"双击聚焦到节点: {target_node.getName()}")
                # 更新选择（如果需要）
                if self.selectedNode != target_node:
                    self.updateSelection(target_node)

                # 执行聚焦
                self.focusCameraOnSelectedNodeAdvanced()
            else:
                print("双击事件：没有有效的目标节点")

        except Exception as e:
            print(f"双击事件处理失败: {e}")

    # 添加一个更精确的双击检测方法

    def checkDoubleClick(self, nodePath):
        """检查是否为双击，返回布尔值"""
        try:
            import time
            current_time = time.time()

            # 必须是同一节点且在时间阈值内
            is_double_click = (self._last_clicked_node == nodePath and
                               nodePath is not None and
                               current_time - self._last_click_time < self._double_click_threshold)

            if is_double_click:
                # 重置状态
                self._last_click_time = 0
                self._last_clicked_node = None
                return True
            else:
                # 更新状态
                self._last_click_time = current_time
                self._last_clicked_node = nodePath
                return False

        except Exception as e:
            print(f"双击检测失败: {e}")
            return False

    # 添加一个定时重置方法，用于清除长时间未完成的双击状态

    def _resetDoubleClickState(self):
        """重置双击状态"""
        self._last_click_time = 0
        self._last_clicked_node = None

    # 添加一个任务来自动重置双击状态

    def _startDoubleClickResetTask(self):
        """启动双击状态重置任务"""
        if self._double_click_task:
            taskMgr.remove(self._double_click_task)

        self._double_click_task = taskMgr.doMethodLater(
            self._double_click_threshold * 2,  # 等待2倍阈值时间
            self._resetDoubleClickStateTask,
            "resetDoubleClickState"
        )

    def _resetDoubleClickStateTask(self, task):
        """任务：重置双击状态"""
        self._resetDoubleClickState()
        self._double_click_task = None
        return task.done

    # 修改 updateSelection 方法以集成双击检测

    # def updateSelection(self, nodePath):
    #     """更新选择状态"""
    #     print(f"\n=== 更新选择状态 ===")
    #
    #     # 如果正在删除节点，避免更新选择
    #     if hasattr(self, '_deleting_node') and self._deleting_node:
    #         print("正在删除节点，跳过选择更新")
    #         print("=== 选择状态更新完成 ===\n")
    #         return
    #
    #     node_name = "None"
    #     if nodePath and not nodePath.isEmpty():
    #         node_name = nodePath.getName()
    #     print(f"新选择的节点: {node_name}")
    #
    #     # 检查是否为双击
    #     is_double_click = self.checkDoubleClick(nodePath)
    #     if is_double_click:
    #         print(f"检测到双击 {node_name}，执行聚焦")
    #         # 启动聚焦（在下一帧执行，确保选择状态已更新）
    #         taskMgr.doMethodLater(0.01, self._delayedFocusTask, "delayedFocus")
    #
    #     self.selectedNode = nodePath
    #     # 添加兼容性属性
    #     self.selectedObject = nodePath
    #
    #     if nodePath and not nodePath.isEmpty():
    #         node_name = nodePath.getName()
    #         print(f"开始为节点 {node_name} 创建选择框和坐标轴...")
    #
    #         # 创建选择框
    #         print("创建选择框...")
    #         self.createSelectionBox(nodePath)
    #         if self.selectionBox:
    #             box_name = "Unknown"
    #             if self.selectionBox and not self.selectionBox.isEmpty():
    #                 box_name = self.selectionBox.getName()
    #             print(f"✓ 选择框创建成功: {box_name}")
    #         else:
    #             print("× 选择框创建失败")
    #
    #         # 创建坐标轴
    #         print("创建坐标轴...")
    #         self.createGizmo(nodePath)
    #         if self.gizmo:
    #             gizmo_name = "Unknown"
    #             if self.gizmo and not self.gizmo.isEmpty():
    #                 gizmo_name = self.gizmo.getName()
    #             print(f"✓ 坐标轴创建成功: {gizmo_name}")
    #         else:
    #             print("× 坐标轴创建失败")
    #
    #         print(f"✓ 选中了节点: {node_name}")
    #     else:
    #         print("清除选择...")
    #         self.clearSelectionBox()
    #         self.clearGizmo()
    #         print("✓ 取消选择")
    #
    #     print("=== 选择状态更新完成 ===\n")

    def _delayedFocusTask(self, task):
        """延迟执行聚焦任务"""
        try:
            self.focusCameraOnSelectedNodeAdvanced()
        except Exception as e:
            print(f"延迟聚焦任务失败: {e}")
        return task.done

    # 添加一个更智能的双击检测方法，考虑鼠标位置

    def checkDoubleClickWithPosition(self, nodePath, mouse_x=None, mouse_y=None):
        """检查是否为双击，同时考虑鼠标位置"""
        try:
            import time
            current_time = time.time()

            # 如果没有提供鼠标位置，直接使用基本双击检测
            if mouse_x is None or mouse_y is None:
                return self.checkDoubleClick(nodePath)

            # 检查节点和时间
            time_diff = current_time - self._last_click_time
            is_same_node = (self._last_clicked_node == nodePath)

            # 如果是同一节点且在时间阈值内，认为是双击
            if is_same_node and time_diff < self._double_click_threshold:
                # 重置状态
                self._last_click_time = 0
                self._last_clicked_node = None
                return True
            else:
                # 更新状态
                self._last_click_time = current_time
                self._last_clicked_node = nodePath
                return False

        except Exception as e:
            print(f"带位置的双击检测失败: {e}")
            return False

    # 添加一个公共方法来设置双击阈值

    def setDoubleClickThreshold(self, threshold_seconds):
        """设置双击时间阈值"""
        if threshold_seconds > 0:
            self._double_click_threshold = threshold_seconds
            print(f"双击阈值已设置为: {threshold_seconds} 秒")
        else:
            print("无效的双击阈值")

    # 添加一个方法来手动触发双击聚焦（可用于测试或其他触发方式）

    def triggerDoubleClickFocus(self, nodePath=None):
        """手动触发双击聚焦"""
        try:
            target_node = nodePath if nodePath else self.selectedNode
            if target_node and not target_node.isEmpty():
                print(f"手动触发聚焦到节点: {target_node.getName()}")
                if self.selectedNode != target_node:
                    self.updateSelection(target_node)
                self.focusCameraOnSelectedNodeAdvanced()
                return True
            else:
                print("没有有效的目标节点进行聚焦")
                return False
        except Exception as e:
            print(f"手动触发聚焦失败: {e}")
            return False

    def cleanup(self):
        """清理选择系统资源"""
        # 清理双击任务
        if self._double_click_task:
            taskMgr.remove(self._double_click_task)
            self._double_click_task = None

        # 清理其他资源
        self.clearSelectionBox()
        self.clearGizmo()
    def clearSelection(self):
        """清除当前选择"""
        try:
            self.selectedNode = None
            self.selectedObject = None
            self.clearSelectionBox()
            self.clearGizmo()

            # 清除树形控件中的选择
            if (hasattr(self.world, 'interface_manager') and
                    self.world.interface_manager and
                    hasattr(self.world.interface_manager, 'treeWidget') and
                    self.world.interface_manager.treeWidget):
                self.world.interface_manager.treeWidget.setCurrentItem(None)

            print("已清除选择")
        except Exception as e:
            print(f"清除选择失败: {e}")

