149 lines
4.3 KiB
Markdown
149 lines
4.3 KiB
Markdown
# 坐标轴系统修复说明
|
||
|
||
## 问题描述
|
||
|
||
用户报告主程序中的坐标轴系统无响应:
|
||
- 鼠标悬停在坐标轴上无高亮效果
|
||
- 点击坐标轴无法开始拖拽
|
||
- 独立的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应用程序 |