EG/demo/坐标轴修复说明.md
2025-12-12 16:16:15 +08:00

4.4 KiB
Raw Permalink Blame History

坐标轴系统修复说明

问题描述

用户报告主程序中的坐标轴系统无响应:

  • 鼠标悬停在坐标轴上无高亮效果
  • 点击坐标轴无法开始拖拽
  • 独立的demo程序standalone_gizmo_test.py)工作正常

根本原因

经过分析发现,问题出在Qt集成环境下的窗口尺寸获取不准确

  1. 独立demo:直接继承自ShowBase,使用self.win.getXSize()self.win.getYSize()获取窗口尺寸是准确的
  2. 主程序使用Qt集成的Panda3DWorld基类,self.win.getXSize()返回的尺寸与实际Qt渲染区域尺寸不匹配
  3. 坐标轴点击检测:依赖屏幕空间投影计算,窗口尺寸错误导致投影坐标计算错误

修复方案

1. 添加Qt部件引用机制

MyWorld类中添加:

# Qt部件引用用于获取准确的渲染区域尺寸
self.qtWidget = None

def setQtWidget(self, widget):
    """设置Qt部件引用"""
    self.qtWidget = widget

def getWindowSize(self):
    """获取准确的窗口尺寸"""
    if self.qtWidget:
        # 优先使用Qt部件的实际尺寸
        width, height = self.qtWidget.getActualSize()
        if width > 0 and height > 0:
            return width, height
    
    # 备用方案使用Panda3D窗口尺寸
    if hasattr(self, 'win') and self.win:
        width = self.win.getXSize()
        height = self.win.getYSize()
        return width, height
    
    # 最后的默认值
    return 800, 600

2. 修改CustomPanda3DWidget

添加方法以获取Qt部件实际尺寸

def getActualSize(self):
    """获取Qt部件的实际渲染尺寸"""
    return (self.width(), self.height())

在构造函数中建立引用:

# 让world引用这个widget以便获取准确的尺寸
if hasattr(world, 'setQtWidget'):
    world.setQtWidget(self)

3. 修改所有屏幕坐标计算

将所有使用self.win.getXSize()self.win.getYSize()的地方改为使用新的getWindowSize()方法:

修改前:

win_x = (screen_pos.getX() + 1.0) * 0.5 * self.win.getXSize()
win_y = (1.0 - screen_pos.getY()) * 0.5 * self.win.getYSize()

修改后:

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

win_x = (screen_pos.getX() + 1.0) * 0.5 * win_width
win_y = (1.0 - screen_pos.getY()) * 0.5 * win_height

4. 修改的关键方法

  1. checkGizmoClick() - 坐标轴点击检测的主方法
  2. updateGizmoHighlight() - 坐标轴悬停高亮
  3. updateGizmoDrag() - 坐标轴拖拽更新
  4. mousePressEventLeft() - 鼠标点击事件处理
  5. mouseMoveEvent() - 鼠标移动事件处理
  6. checkGizmoClickFallback() - 备用点击检测方法

技术原理

屏幕空间投影算法

坐标轴点击检测使用以下步骤:

  1. 世界坐标 → 相机坐标

    cam_space_pos = self.cam.getRelativePoint(self.render, world_pos)
    
  2. 相机坐标 → 标准化屏幕坐标

    screen_pos = Point2()
    self.cam.node().getLens().project(cam_space_pos, screen_pos)
    
  3. 标准化坐标 → 像素坐标

    win_x = (screen_pos.getX() + 1.0) * 0.5 * win_width
    win_y = (1.0 - screen_pos.getY()) * 0.5 * win_height
    
  4. 点到线段距离计算

    distance = self.distanceToLine((mouse_x, mouse_y), center_screen, axis_screen)
    

窗口尺寸差异的影响

如果窗口尺寸不准确:

  • 步骤3中的像素坐标计算错误
  • 坐标轴在屏幕上的投影位置计算错误
  • 鼠标点击位置与计算出的轴位置不匹配
  • 导致点击检测失败

测试验证

创建了测试脚本demo/test_size_fix.py来验证修复效果:

  • 对比Qt部件尺寸和Panda3D窗口尺寸
  • 实时监控尺寸变化
  • 验证getWindowSize()方法的正确性

预期效果

修复后的坐标轴系统应该:

  1. 鼠标悬停时正确高亮对应的轴红色X轴、绿色Y轴、蓝色Z轴变为黄色
  2. 点击坐标轴能够正确开始拖拽
  3. 拖拽时只沿选定轴方向移动物体
  4. 松开鼠标时正确结束拖拽

兼容性

修复方案完全向后兼容:

  • 如果没有Qt环境自动回退到使用Panda3D窗口尺寸
  • 如果Qt部件引用未设置使用备用方案
  • 不影响独立的ShowBase应用程序