EG/demo/坐标轴修复说明.md
2025-07-02 09:49:59 +08:00

149 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 坐标轴系统修复说明
## 问题描述
用户报告主程序中的坐标轴系统无响应:
- 鼠标悬停在坐标轴上无高亮效果
- 点击坐标轴无法开始拖拽
- 独立的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`类中添加:
```python
# 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部件实际尺寸
```python
def getActualSize(self):
"""获取Qt部件的实际渲染尺寸"""
return (self.width(), self.height())
```
在构造函数中建立引用:
```python
# 让world引用这个widget以便获取准确的尺寸
if hasattr(world, 'setQtWidget'):
world.setQtWidget(self)
```
### 3. 修改所有屏幕坐标计算
将所有使用`self.win.getXSize()`和`self.win.getYSize()`的地方改为使用新的`getWindowSize()`方法:
#### 修改前:
```python
win_x = (screen_pos.getX() + 1.0) * 0.5 * self.win.getXSize()
win_y = (1.0 - screen_pos.getY()) * 0.5 * self.win.getYSize()
```
#### 修改后:
```python
# 获取准确的窗口尺寸
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. **世界坐标 → 相机坐标**
```python
cam_space_pos = self.cam.getRelativePoint(self.render, world_pos)
```
2. **相机坐标 → 标准化屏幕坐标**
```python
screen_pos = Point2()
self.cam.node().getLens().project(cam_space_pos, screen_pos)
```
3. **标准化坐标 → 像素坐标**
```python
win_x = (screen_pos.getX() + 1.0) * 0.5 * win_width
win_y = (1.0 - screen_pos.getY()) * 0.5 * win_height
```
4. **点到线段距离计算**
```python
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应用程序