4.4 KiB
4.4 KiB
自动适配:点阵不越界 + 面板尺寸跟随点阵
这份文档说明本项目里为了实现:
- 圆点(点阵)始终落在面板顶面上,不越界
- 面板的宽/深(W/D)根据点阵规格自动缩放
- 面板厚度(H)保持独立可控(不随点阵改变)
所做的代码改动和使用方式。
现象 & 根因
当前点阵的摆放逻辑(src/glwidget.cpp 的 GLWidget::updateInstanceBufferIfNeeded_())是:
- 点阵中心围绕原点居中摆放
- 每个点的中心坐标:
x = c * pitch - (cols-1)*pitch/2z = r * pitch - (rows-1)*pitch/2
- 所以 点中心 的范围一定在:
x ∈ [-gridW/2, +gridW/2],其中gridW = (cols-1)*pitchz ∈ [-gridD/2, +gridD/2],其中gridD = (rows-1)*pitch
但每个点是“有半径”的(dotRadius),在 shader 里实际画出来的圆会占据:
x方向还会向外多出dotRadiusz方向还会向外多出dotRadius
如果面板的 W/D 没有随点阵变化而调整,就会出现你截图里那种:外圈点的圆形被画到了面板外(看起来像“越界”)。
解决思路(数学上怎么保证不越界)
要让“圆点的边缘”也不越界,需要满足:
- 面板半宽
panelW/2 >= gridW/2 + dotRadius - 面板半深
panelD/2 >= gridD/2 + dotRadius
等价于:
panelW >= gridW + 2*dotRadiuspanelD >= 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) * pitchgridD = (rows-1) * pitchm_panelW = gridW + 2*dotRadiusm_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_panelGeometryDirtym_dotsGeometryDirty
- 在
GLWidget::paintGL()的开头判断 dirty:- dirty 就调用
initPanelGeometry_()/initDotGeometry_()重建 - dots 重建后会重新分配 instanceVBO,所以把
m_valuesDirty = true,确保下一帧重新上传 instance 数据
- dirty 就调用
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 覆盖掉,越界问题就会回来。
使用方式(你应该怎么调用)
推荐顺序:
glw->setSpec(rows, cols, pitch, dotRadius);- 会自动算出
panelW/panelD,保证点不越界
- 会自动算出
glw->setPanelThickness(panelH);- 只改厚度,不影响自动宽深
glw->setRange(minV, maxV);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。