first commit
This commit is contained in:
151
AUTO_PANEL_SIZING.md
Normal file
151
AUTO_PANEL_SIZING.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# 自动适配:点阵不越界 + 面板尺寸跟随点阵
|
||||
|
||||
这份文档说明本项目里为了实现:
|
||||
|
||||
1. **圆点(点阵)始终落在面板顶面上,不越界**
|
||||
2. **面板的宽/深(W/D)根据点阵规格自动缩放**
|
||||
3. **面板厚度(H)保持独立可控(不随点阵改变)**
|
||||
|
||||
所做的代码改动和使用方式。
|
||||
|
||||
---
|
||||
|
||||
## 现象 & 根因
|
||||
|
||||
当前点阵的摆放逻辑(`src/glwidget.cpp` 的 `GLWidget::updateInstanceBufferIfNeeded_()`)是:
|
||||
|
||||
- 点阵中心围绕原点居中摆放
|
||||
- 每个点的中心坐标:
|
||||
- `x = c * pitch - (cols-1)*pitch/2`
|
||||
- `z = r * pitch - (rows-1)*pitch/2`
|
||||
- 所以 **点中心** 的范围一定在:
|
||||
- `x ∈ [-gridW/2, +gridW/2]`,其中 `gridW = (cols-1)*pitch`
|
||||
- `z ∈ [-gridD/2, +gridD/2]`,其中 `gridD = (rows-1)*pitch`
|
||||
|
||||
但每个点是“有半径”的(`dotRadius`),在 shader 里实际画出来的圆会占据:
|
||||
|
||||
- `x` 方向还会向外多出 `dotRadius`
|
||||
- `z` 方向还会向外多出 `dotRadius`
|
||||
|
||||
如果面板的 `W/D` 没有随点阵变化而调整,就会出现你截图里那种:**外圈点的圆形被画到了面板外**(看起来像“越界”)。
|
||||
|
||||
---
|
||||
|
||||
## 解决思路(数学上怎么保证不越界)
|
||||
|
||||
要让“圆点的边缘”也不越界,需要满足:
|
||||
|
||||
- 面板半宽 `panelW/2 >= gridW/2 + dotRadius`
|
||||
- 面板半深 `panelD/2 >= gridD/2 + dotRadius`
|
||||
|
||||
等价于:
|
||||
|
||||
- `panelW >= gridW + 2*dotRadius`
|
||||
- `panelD >= gridD + 2*dotRadius`
|
||||
|
||||
本项目采用的就是这条公式。
|
||||
|
||||
> 如果你希望四周再留白一点,只要把公式改成:`+ 2*(dotRadius + margin)` 即可。
|
||||
|
||||
---
|
||||
|
||||
## 代码改动点(做了什么)
|
||||
|
||||
### 1) `setSpec()` 里自动计算面板 W/D
|
||||
|
||||
文件:`src/glwidget.cpp`,函数:`GLWidget::setSpec(...)`
|
||||
|
||||
做的事:
|
||||
|
||||
- 对 `rows/cols/pitch/dotRadius` 做了下限保护(避免负数)
|
||||
- 根据点阵规格计算面板顶面 `m_panelW/m_panelD`:
|
||||
- `gridW = (cols-1) * pitch`
|
||||
- `gridD = (rows-1) * pitch`
|
||||
- `m_panelW = gridW + 2*dotRadius`
|
||||
- `m_panelD = gridD + 2*dotRadius`
|
||||
- 标记面板几何和 dots 几何需要重建(dirty flag)
|
||||
|
||||
这样一旦你改点阵规格,面板会自动缩放到刚好包住点阵。
|
||||
|
||||
---
|
||||
|
||||
### 2) 厚度单独控制:`setPanelThickness()`
|
||||
|
||||
文件:`src/glwidget.h` / `src/glwidget.cpp`
|
||||
|
||||
新增接口:
|
||||
|
||||
- `void setPanelThickness(float h);`
|
||||
|
||||
它只修改 `m_panelH`(厚度),并标记面板几何需要重建。
|
||||
|
||||
---
|
||||
|
||||
### 3) 由于 W/D 会变:需要“重建几何体 buffer”
|
||||
|
||||
原因:面板顶面矩形的顶点坐标依赖 `m_panelW/m_panelD`,改变后必须重建 VBO/IBO/VAO 才能反映到 GPU。
|
||||
|
||||
做法:
|
||||
|
||||
- 新增两个 dirty flag:
|
||||
- `m_panelGeometryDirty`
|
||||
- `m_dotsGeometryDirty`
|
||||
- 在 `GLWidget::paintGL()` 的开头判断 dirty:
|
||||
- dirty 就调用 `initPanelGeometry_()` / `initDotGeometry_()` 重建
|
||||
- dots 重建后会重新分配 instanceVBO,所以把 `m_valuesDirty = true`,确保下一帧重新上传 instance 数据
|
||||
|
||||
---
|
||||
|
||||
### 4) `initPanelGeometry_()` / `initDotGeometry_()` 现在会先删旧 buffer
|
||||
|
||||
因为会重复调用重建,所以这两个函数里加入了:
|
||||
|
||||
- 先 `glDeleteBuffers` / `glDeleteVertexArrays`
|
||||
- 再重新 `glGen*` 创建新资源
|
||||
|
||||
保证不会重复堆积资源(泄漏)或引用旧 VAO。
|
||||
|
||||
---
|
||||
|
||||
### 5) `main.cpp` 不再手动写死面板 W/D
|
||||
|
||||
文件:`main.cpp`
|
||||
|
||||
把原来的:
|
||||
|
||||
- `glw->setPanelSize(1.2f, 0.08f, 1.2f);`
|
||||
|
||||
改为:
|
||||
|
||||
- `glw->setPanelThickness(0.08f);`
|
||||
|
||||
原因:如果继续调用 `setPanelSize()`,你会把 `setSpec()` 自动算出来的 W/D 覆盖掉,越界问题就会回来。
|
||||
|
||||
---
|
||||
|
||||
## 使用方式(你应该怎么调用)
|
||||
|
||||
推荐顺序:
|
||||
|
||||
1. `glw->setSpec(rows, cols, pitch, dotRadius);`
|
||||
- 会自动算出 `panelW/panelD`,保证点不越界
|
||||
2. `glw->setPanelThickness(panelH);`
|
||||
- 只改厚度,不影响自动宽深
|
||||
3. `glw->setRange(minV, maxV);`
|
||||
4. `glw->submitValues(values);`
|
||||
|
||||
如果你确实想手动指定面板大小(不走自动适配),可以再调用:
|
||||
|
||||
- `glw->setPanelSize(w, h, d);`
|
||||
|
||||
但要理解:这会覆盖自动计算的 `W/D`,点可能再次越界(除非你按本文公式算好)。
|
||||
|
||||
---
|
||||
|
||||
## 可选改进(按你需求继续拓展)
|
||||
|
||||
- **加边距(不要贴边)**:在 `setSpec()` 里引入 `margin`,把公式改成:
|
||||
`m_panelW = gridW + 2*(m_dotRadius + margin)`
|
||||
`m_panelD = gridD + 2*(m_dotRadius + margin)`
|
||||
- **让点阵填满面板但不改变 pitch**:目前面板跟着 pitch 走;如果你希望面板固定而 pitch 自适配,需要反过来解 pitch。
|
||||
|
||||
Reference in New Issue
Block a user