修复右键网格菜单后悬停提示残留;修复实验性功能预览曲线导致的平移/拖光标卡顿
1) 右键弹出网格菜单前清空绘图区悬停状态(并让 OnMouseLeave 一并清 m_hoverLegend),菜单打开后信号光晕与「可拖拽」提示气泡不再残留。 2) 时间光标处派生取值胶囊原先把请求窗口取成「可见区间 ∪ 光标下标」的并集:光标在视界外时窗口达上千万采样,超缓存上限后每帧重算整段多次迭代滑动平均(实测单帧 300ms+),并随平移/拖滑块/拖光标/改色一起变卡。改为只请求光标所在采样点的小窗口,取值依赖由 CurveSpanFor 保护带覆盖,取值逐位一致、显示效果零变化。 离屏实测 VERY_BIG_DATA:均值滤波平移 326ms→16~23ms,中心轴边沿计数平移 300ms→20ms。
This commit is contained in:
1 parent
2322e76d8c
commit
2f7a6bc2d2
3 files changed
+54
-11
No files matched your search
@@ -2570,6 +2570,10 @@ namespace jdi
|
||||
const int laneIndex = m_plot.LaneIndexAt(x, y);
|
||||
if (laneIndex >= 0)
|
||||
{
|
||||
// 菜单打开后鼠标移动被菜单独占,绘图区不再收到移动事件;
|
||||
// 若不先清掉悬停状态,右键前停留在曲线上的「可拖拽光晕」与提示气泡
|
||||
// 会一直挂在原地(用户报告的"进入网格菜单后高亮/文本提示不消失")。
|
||||
m_plot.OnMouseLeave();
|
||||
ShowPlotContextMenu(laneIndex, x, y);
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
namespace jdi
|
||||
{
|
||||
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
const int kTimeTickTarget = 10; /**< 时间轴期望刻度数。 */
|
||||
@@ -4761,6 +4759,12 @@ namespace jdi
|
||||
m_hoverSignal = SignalRef();
|
||||
changed = true;
|
||||
}
|
||||
// 图例悬停半高亮同样随鼠标离开而消失(否则右键弹菜单/移出绘图区后残留在原地)。
|
||||
if (m_hoverLegend.IsValid())
|
||||
{
|
||||
m_hoverLegend = SignalRef();
|
||||
changed = true;
|
||||
}
|
||||
if (m_cursorHover)
|
||||
{
|
||||
m_cursorHover = false;
|
||||
@@ -7372,17 +7376,21 @@ namespace jdi
|
||||
}
|
||||
const int otherIndex =
|
||||
showDelta ? FindNearestSample(*resolved.file, otherTime) : -1;
|
||||
const std::size_t count =
|
||||
std::min(resolved.file->time.size(), resolved.data->values.size());
|
||||
std::size_t begin = 0;
|
||||
std::size_t end = 0;
|
||||
VisibleIndexRange(*resolved.file, count, begin, end);
|
||||
begin = std::min(begin, static_cast<std::size_t>(sampleIndex));
|
||||
end = std::max(end, static_cast<std::size_t>(sampleIndex) + 1);
|
||||
// 本函数**只需要光标所在采样点处的派生取值**(以及双光标另一端的取值),
|
||||
// 因此把请求窗口收窄到「需要的那几个下标」即可,均值滤波半径造成的取值
|
||||
// 依赖由 `CurveSpanFor` 自身的保护带负责覆盖。
|
||||
//
|
||||
// ⚠️ 绝不能把窗口扩成「光标 ↔ 视界边缘」的并集:光标停在视界之外(例如
|
||||
// 平移视界把光标甩到很远,或光标远离当前缩放区间)时会得到一个上千万采样的
|
||||
// 巨大窗口,超过缓存上限后走一次性缓冲 → **每帧重算整段滤波**,
|
||||
// 实测单帧 300ms+(用户报告的"平移/拖动光标异常卡顿"的直接原因)。
|
||||
std::size_t begin = static_cast<std::size_t>(sampleIndex);
|
||||
std::size_t end = begin + 1;
|
||||
if (otherIndex >= 0)
|
||||
{
|
||||
begin = std::min(begin, static_cast<std::size_t>(otherIndex));
|
||||
end = std::max(end, static_cast<std::size_t>(otherIndex) + 1);
|
||||
const std::size_t other = static_cast<std::size_t>(otherIndex);
|
||||
begin = std::min(begin, other);
|
||||
end = std::max(end, other + 1);
|
||||
}
|
||||
std::size_t spanBegin = 0;
|
||||
const std::vector<float>* curvePtr =
|
||||
|
||||
@@ -157,6 +157,36 @@ Documents/ 项目文档(含本文件)
|
||||
- 主菜单条目矩形的右缘为 `menu.right - kPadH`,而子菜单左缘为 `menu.right - kSubOverlap`,两者之间存在约 3px 的**内边距"死区"**(位于主菜单矩形内但落在任何条目之外)。鼠标事件密集时指针必然落入该窄带,从而触发误关闭。
|
||||
- **修复**:子菜单展开期间,若指针仍位于「主菜单 ∪ 子菜单」矩形范围内、且尚未落到**其它可点击条目**上(例如掠过内边距窄带、分隔线、禁用项),则保持子菜单展开并维持父项高亮;只有明确移动到其它条目或完全离开上述范围时才收起。
|
||||
|
||||
### 9.2 打开网格右键菜单后,悬停光晕与「可拖拽」提示气泡不消失(已修复)
|
||||
- **症状**:鼠标停在信号曲线上(出现光晕高亮 + 「XXX · 按住拖拽可移入其它网格」提示气泡)时右键弹出网格菜单,高亮与气泡**残留在原地**,不随菜单打开而消失。
|
||||
- **根因**:菜单打开后鼠标移动被菜单独占(`Application::HandleMessage` 在 `m_menu.IsOpen()` 时只把事件交给菜单),绘图区再也收不到 `WM_MOUSEMOVE`;而打开菜单时**没有清空**绘图区的悬停状态(`m_hoverSignal` / `m_hoverLegend`),因此 `PaintHoverHighlight` 与 `PaintSignalInteraction` 继续把高亮与气泡画出来。
|
||||
- **修复**:
|
||||
- `Application` 在绘图区右键弹出网格菜单前调用 `m_plot.OnMouseLeave()`,清空悬停状态;
|
||||
- `PlotView::OnMouseLeave()` 补上清空 `m_hoverLegend`(原先只清 `m_hoverLane/m_hoverSignal` 等,图例半高亮会残留)。
|
||||
|
||||
### 9.3 性能:启用带预览曲线的实验性功能后,平移/拖动光标异常卡顿(已修复)
|
||||
- **症状**(以 `VERY_BIG_DATA.CSV` 为例,加载并显示全部信号后):时间轴选区放大到某些区间时,左键/中键平移网格视界、或启用「均值滤波 / 中心轴边沿计数」后再平移、甚至只是左右拖动时间光标看数值,都会**异常卡顿**(实测单帧 300ms+)。
|
||||
- **排查方法**:在 `.preflight/perf/`(gitignored)用离屏窗口(不占用鼠标/键盘/屏幕)直接驱动 `PlotView`,用 `QueryPerformanceCounter` 测 `Paint()`,并对 `CurveSpanFor` / `ComputeMovingAverageIterated` / `PaintDerivedCurve` 分段埋点计时。
|
||||
- **根因**(`Components/PlotView/Src/PlotView.cpp` 的 `PaintCursor` → `paintDerivedCapsule` lambda):
|
||||
- 绘制时间光标处的**派生曲线取值胶囊**时,把请求窗口取成「**可见区间 ∪ 光标采样下标**」的并集;
|
||||
- 当光标不在当前视界内(平移视界会把光标甩到视界外)时,这个并集会变成 `[光标下标, 视界右端]` 或 `[视界左端, 光标下标]` —— 上千万采样的**巨大窗口**;
|
||||
- 该窗口超过 `kCurveSpanCacheMaxSamples`(4M) → 走一次性缓冲、**不进缓存** → **每一帧都重算整段多次迭代滑动平均**(实测 5 条曲线合计 300ms+)。
|
||||
- 这条路径同样被平移、拖滑块、拖动光标、改色等所有「每帧重绘」的交互触发,因此它们一起变卡。
|
||||
- **修复**:`paintDerivedCapsule` 只需要光标所在采样点(以及双光标另一端)的派生取值,因此把请求窗口收窄为「**需要的那几个下标**」(`[sampleIndex, sampleIndex+1]`,双光标时并入另一端)。均值滤波半径造成的取值依赖由 `CurveSpanFor` 自身的保护带(`guard = clamp(max(窗口/8, (X+1)*N+64), 4096, 65536)`)负责覆盖 —— 对一切 `reach ≤ 65536` 的参数,取值与「整条曲线」**逐位一致**,显示效果零变化。
|
||||
- **效果**(离屏实测,`VERY_BIG_DATA`,5 网格):
|
||||
- 均值滤波平移(2% 跨度):**326ms → 约 16–23ms**;
|
||||
- 均值滤波平移(0.4%/0.08%/0.016%):**约 305ms → 4–6ms**;
|
||||
- 中心轴边沿计数平移(2%):**约 300ms → 约 20ms**;
|
||||
- 光标左右移动(静态场景):命中场景缓存,仅叠加层重绘,约 9ms。
|
||||
- **不影响显示的说明**:本次**未**改动任何几何/绘制算法(没有引入曾导致"棘状细节被并成实心块"的 ±1 列扩展),只收窄了「为了取时间光标处派生取值而请求的曲线窗口」,因此画面与实际取值完全不变。
|
||||
|
||||
### 9.4 性能经验与已知残留热点(供后续参考)
|
||||
- **本仓库的性能黄金法则**:交互期间(`m_dragMode != NONE`)走「快速帧」(关闭波形/派生曲线抗锯齿),松手后补一帧全质量;静止画面走「静态场景缓存」(BitBlt 贴图 ~1ms)。任何**只在鼠标移动/拖动参数时改变**的内容都必须画在贴图之后(叠加层),绝不能进静态缓存,也不能每帧触发整条曲线/整段采样的重算。
|
||||
- **已知残留热点**(本次未动,均为"非拖拽的整幅重绘",实际拖拽时因快速帧而很快):
|
||||
- 原始波形的「逐像素列包络带」用 GDI+ `FillPath` 填充;在某些跨度下包络带不被判为「梳状」,抗锯齿填充仍约 20–27ms/帧(`VERY_BIG_DATA` 的全量重绘)。这是 GDI+ 抗锯齿路径的固有代价;如需进一步提速,**只能**降低列分辨率或对该形态关闭抗锯齿 —— 两者都会改变显示效果,需用户确认后再动。
|
||||
- 「构建为新信号」等一次性操作会真正计算整条派生曲线(约 1 帧的秒级耗时,正常)。
|
||||
- **测量工具**:`.preflight/perf/perf.exe <csv> [mode]`(离屏,不弹窗)。`mode=0` 跑各交互场景耗时;`mode=1` 导出确定性渲染 PNG 以便逐像素比对(用于验证"显示效果不变")。该目录已由 `.gitignore` 忽略,不会入库。
|
||||
|
||||
---
|
||||
|
||||
## 10. 变更日志(本文件维护记录)
|
||||
@@ -164,3 +194,4 @@ Documents/ 项目文档(含本文件)
|
||||
| 日期 | 设备/模型 | 摘要 |
|
||||
| --- | --- | --- |
|
||||
| 2026-10-11 | DeepSeek-V4.1-Flash | 新建本文件;记录项目要点与规范;修复「实验性功能」二级菜单瞬间关闭问题。 |
|
||||
| 2026-10-11 | DeepSeek-V4.1-Flash | 修复右键网格菜单后悬停光晕/提示气泡残留;修复「启用带预览曲线的实验性功能后平移/拖光标 300ms+ 卡顿」(`paintDerivedCapsule` 巨窗每帧重算,收窄请求窗口,显示效果零变化)。 |
|
||||
Reference in new issue
Block a user